1 Introduction

A comparison of JULES-ES-1p0 wave01 members against the original ensemble (wave00).

Wave 01 input parameter sets were picked using History matching to fall within Andy Wiltshire’s basic constraints on NBP, NPP, cSoil and cVeg stocks at the end of the 20th century. We use 300 of the 500 members, keeping back 2/5ths for emulator validation later.

We answer some basic questions.

What proportion of the new ensemble match AW’s constraints?

What do the timeseries of carbon cycle properties look like with and without AW’s constraints?

How good is a GP emulator? Does it get better overall with the new ensemble members added? In particular, does it get better for those members within the AW constraints?

Does comparison of the ensemble with Atmospheric growth observations give more of a constraint?

To do:

Does the sensitivity analysis change?

2 Preliminaries

Load libraries, functions and data.


ensloc <- '/scratch/hadaw/jules_postprocess/u-ck006/'

makeJulesEnsembleModernValue <- function(ensloc, varlist, nstart, nend, ix = 144:164){
  
  nens <- (nend - nstart) + 1
  datmat <- matrix(nrow = nens, ncol = length(varlist))
  colnames(datmat) <- varlist
  
  enslist <- paste("P", formatC(nstart:nend, width=4, flag="0"), sep="")
  
  for(i in 1:nens){
    
    vec <- rep(NA, length(varlist))
    
    ensmember <- enslist[i] 
    
    fn <- paste0(ensloc,ensmember,'/stats/','JULES-ES-1p0_',ensmember,'_Annual_global.nc')
    
    try(nc <- nc_open(paste0(fn)))
    try(vec <- sapply(varlist, FUN = modernValue, nc = nc, ix = ix))
    datmat[i, ] <- vec
    nc_close(nc)
  }
  return(list(datmat = datmat, enslist = enslist))
}
  
nstart <- 499
nend <- 798

#if (file.exists("ensemble_wave01.rdata")) {
#  load("ensemble_wave01.rdata")
#} else {
  
ens_wave01_mv <- makeJulesEnsembleModernValue(ensloc = '/scratch/hadaw/jules_postprocess/u-ck006/', 
                                              varlist = y_names_sum,
                                              nstart = nstart,
                                              nend = nend, 
                                              ix = 144:164) 
  
#  save(ens_wave01_mv, file ="ensemble_wave01.rdata")
#}
  

lhs_wave01 <- read.table( '../conf_files_augment_JULES-ES-1p0/lhs_example.txt', header = TRUE)

X_wave01 = normalize(lhs_wave01, wrt = rbind(lhs_i, lhs_ii, lhs_wave01))
colnames(X_wave01) = colnames(lhs_wave01)

# Match the 300 outputs we're using in the training data
X_wave01_train <- X_wave01[1:300,]
Y_const_wave01 <- ens_wave01_mv$datmat[, ynames_const]

Y_const_wave01_scaled <- sweep(Y_const_wave01, 2, STATS = scalevec, FUN = '/' )

2.1 How many run failures were there?

There are no NAs but some relative humidity values are infinite. There are no “low NPP” ensemble members


low_npp_ix_wave01 <- which(ens_wave01_mv$datmat[,'npp_nlim_lnd_sum'] < 1e5)

min(ens_wave01_mv$datmat[,'npp_nlim_lnd_sum'])
[1] 117464.6
Y_wave01_nlevel0_ix <- which(is.na(ens_wave01_mv$datmat[,'nbp_lnd_sum']))

all(is.finite(ens_wave01_mv$datmat))
[1] FALSE
which(!is.finite(ens_wave01_mv$datmat), arr.ind = TRUE)
     row col
[1,] 140   9
[2,] 232   9
[3,] 249   9
[4,] 300   9
ens_wave01_mv$datmat[which(!is.finite(ens_wave01_mv$datmat), arr.ind = TRUE)]
[1] Inf Inf Inf Inf
colnames(ens_wave01_mv$datmat)[9]
[1] "rh_lnd_sum"

3 Ensemble behaviour in key (constraining) outputs.

Global mean for the 20 years at the end of the 20th Century. There is still a significant low bias on cVeg output.


wave00col <- 'skyblue2'
wave01col <- 'tomato2'

wave00col <- 'dodgerblue2'
wave01col <- 'firebrick'
rangecol <- 'grey'


# Histogram of level 1 constraints
hcol = 'darkgrey'
lcol = 'black'

#pdf(file = 'figs/level_2_constraints_hists.pdf', width = 6, height = 5)
par(mfrow = c(2,2), fg = 'darkgrey', las = 1, oma = c(0.1, 0.1, 4, 0.1))

trunc <- function(x, vec){
  
  dat <- x[x < max(vec) & x > min(vec)  ]
  
  dat
  
}


h <- hist(Y_const_level1a_scaled[,'nbp_lnd_sum'], main = 'NBP', xlab = 'GtC/year', col = makeTransparent(wave00col,150))
hist(trunc(Y_const_wave01_scaled [,'nbp_lnd_sum'], h$breaks) ,
     col = makeTransparent(wave01col,150) , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['nbp_lnd_sum'], lwd = 2)

polygon(x = c(0, 100, 100, 0), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
        border = makeTransparent(rangecol))

h <- hist(Y_const_level1a_scaled[,'npp_nlim_lnd_sum'],col = makeTransparent(wave00col,150), main = 'NPP', xlab = 'GtC/year')
hist(trunc(Y_const_wave01_scaled [,'npp_nlim_lnd_sum'], h$breaks) , 
     col = makeTransparent(wave01col) , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['npp_nlim_lnd_sum'], lwd = 2)

polygon(x = c(35, 80, 80, 35), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
        border = makeTransparent(rangecol))


h <- hist(Y_const_level1a_scaled[,'cSoil_lnd_sum'], col = makeTransparent(wave00col,150), main = 'Soil Carbon', xlab = 'GtC')
hist(trunc(Y_const_wave01_scaled [,'cSoil_lnd_sum'], h$breaks) , 
     col = makeTransparent(wave01col,150) , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['cSoil_lnd_sum'], lwd = 2)

polygon(x = c(750, 3000, 3000, 750), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
        border = makeTransparent(rangecol))

h <- hist(Y_const_level1a_scaled[,'cVeg_lnd_sum'], col = makeTransparent(wave00col,150), main = 'Vegetation Carbon', xlab = 'GtC')
hist(trunc(Y_const_wave01_scaled [,'cVeg_lnd_sum'], h$breaks) , 
   col = makeTransparent(wave01col,150)  , breaks = h$breaks, add = TRUE)

rug(Y_const_stan_scaled['cVeg_lnd_sum'], lwd = 2)

polygon(x = c(300, 800, 800, 300), y = c(0, 0, 1000, 1000),
        col = makeTransparent(rangecol, 60),
       border =  makeTransparent(rangecol))

#dev.off()

reset()

legend('top', horiz = TRUE, fill = c(makeTransparent(wave00col, 150), makeTransparent(wave01col, 150), makeTransparent(rangecol, 60)), legend = c('Wave00', 'Wave01', 'AW range'))

3.1 What proportion of models now fall within Andy’s constraints?

A third! Better than before, but still not great. Pointing at a significant model discrepency in cVeg

AW_constraints <- matrix(nrow = 2, ncol = length(ynames_const))

AW_constraints[1,] <- c(0, 35, 750, 300)
AW_constraints[2,] <- c(100, 80, 3000, 800)

colnames(AW_constraints) <- ynames_const
rownames(AW_constraints) <- c('min', 'max')


# conform to Andy's basic constraints
#level2_ix_wave01 <- which(apply(Y_const_wave01_scaled, 1, FUN = withinRange, maxes  = AW_constraints[2,], mins = AW_constraints[1,] ))

#nlevel2_ix_wave01 <- which(apply(Y_const_wave01_scaled, 1, FUN = withinRange, maxes  = AW_constraints[2,], mins = AW_constraints[1,] ) == FALSE)

level2_ix_wave01 <- which(Y_const_wave01_scaled[,'nbp_lnd_sum'] > 0 &
                    Y_const_wave01_scaled[,'npp_nlim_lnd_sum'] > 35 & Y_const_wave01_scaled[,'npp_nlim_lnd_sum'] < 80 &
                    Y_const_wave01_scaled[,'cSoil_lnd_sum'] > 750 & Y_const_wave01_scaled[,'cSoil_lnd_sum'] < 3000 &
                  Y_const_wave01_scaled[,'cVeg_lnd_sum'] > 300 & Y_const_wave01_scaled[,'cVeg_lnd_sum'] < 800
                  )

Of the 300 members of the wave01 ensemble, 100 pass Andy Wiltshire’s Level 2 constraints.


length(level2_ix_wave01)
[1] 100

Pairs plot of the inputs that pass the constraints with respect to the limits of the original ensemble.


pairs(X_wave01[level2_ix_wave01, ],
      xlim = c(0,1),
      ylim = c(0,1),
      gap = 0,
      lower.panel = NULL,
      pch = 20,
      col = makeTransparent(wave01col,200)
      )

3.2 Timeseries of mean carbon cycle properties over whole run.


## ----------------------------------------------------------------------
## Load ensemble
## ----------------------------------------------------------------------

if (file.exists("ensemble_timeseries_wave01.rdata")) {
  load("ensemble_timeseries_wave01.rdata")
} else {
  
  # primary carbon cycle outputs
  npp_ens <- makeTimeseriesEnsemble(variable = "npp_nlim_lnd_sum") / (1e12/ysec)
  nbp_ens <-  makeTimeseriesEnsemble(variable = "nbp_lnd_sum") / (1e12/ysec)
  cSoil_ens <-  makeTimeseriesEnsemble(variable = "cSoil_lnd_sum") / 1e12
  cVeg_ens <-  makeTimeseriesEnsemble(variable = "cVeg_lnd_sum") / 1e12
  
  
  lai_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "lai_lnd_mean")
  
  # fluxes
  rh_lnd_sum_ens <- makeTimeseriesEnsemble(variable = "rh_lnd_sum") / (1e12/ysec)
  fLuc_lnd_sum_ens <- makeTimeseriesEnsemble(variable = "fLuc_lnd_sum") / (1e12/ysec)
  fHarvest_lnd_sum_ens <- makeTimeseriesEnsemble(variable = "fHarvest_lnd_sum") / (1e12/ysec)
  
  
  # fractions
  treeFrac_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "treeFrac_lnd_mean")
  shrubFrac_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "shrubFrac_lnd_mean")
  baresoilFrac_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "baresoilFrac_lnd_mean")
  #c3PftFrac_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "c3PftFrac_lnd_mean_ens")
  #c4PftFrac_lnd_mean_ens <- makeTimeseriesEnsemble(variable = "c4PftFrac_lnd_mean_ens")
  
  save(npp_ens, nbp_ens, cSoil_ens, cVeg_ens, lai_lnd_mean_ens, rh_lnd_sum_ens, fLuc_lnd_sum_ens, fHarvest_lnd_sum_ens,
       treeFrac_lnd_mean_ens, shrubFrac_lnd_mean_ens, baresoilFrac_lnd_mean_ens, file = "ensemble_timeseries_wave01.rdata" )
  
}

total_land_carbon_ens <- cSoil_ens + cVeg_ens


makeTimeseriesEnsemble <- function(ensloc, variable, nstart, nend, nts = 164, cn = 1850:2013){
  
  nens <- (nend - nstart) + 1
  # nens is number of ensemble members
  # nts length of timeseries
  # cn is colnames()
  datmat <- matrix(NA, nrow = nens, ncol = nts)
  colnames(datmat) <- cn
  
  enslist <- paste("P", formatC(nstart:nend, width=4, flag="0"), sep="")
  #floc <- paste0(ensloc,ensmember,subdir)
  
  for(i in 1:nens){
    
    vec <- rep(NA,nts)
    
    ensmember <- enslist[i] 
    
    fn <- paste0(ensloc,ensmember,'/stats/','JULES-ES-1p0_',ensmember,'_Annual_global.nc')
    
    
    try(nc <- nc_open(paste0(fn)))
    try(dat <- extractTimeseries(nc, variable))
    
    datmat[i, ] <- dat
    nc_close(nc)
  }
  datmat
}

nstart <- 499
nend <- 798

if (file.exists("ensemble_timeseries_wave01.rdata")) {
  load("ensemble_timeseries_wave01.rdata")
} else {
  
  # primary carbon cycle outputs
   npp_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend, variable = "npp_nlim_lnd_sum") / (1e12/ysec)
   nbp_ens_wave01 <-  makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,variable = "nbp_lnd_sum") / (1e12/ysec)
   cSoil_ens_wave01 <-  makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,variable = "cSoil_lnd_sum") / 1e12
   cVeg_ens_wave01 <-  makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,variable = "cVeg_lnd_sum") / 1e12
  # 
  # 
   lai_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,variable = "lai_lnd_mean")
  # 
  # # fluxes
   rh_lnd_sum_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend, variable = "rh_lnd_sum") / (1e12/ysec)
   fLuc_lnd_sum_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend, variable = "fLuc_lnd_sum") / (1e12/ysec)
   fHarvest_lnd_sum_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend, variable = "fHarvest_lnd_sum") / (1e12/ysec)
  # 
  # 
  # # fractions
   treeFrac_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,  variable = "treeFrac_lnd_mean")
   shrubFrac_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,  variable = "shrubFrac_lnd_mean")
   baresoilFrac_lnd_mean_ens_wave01 <- makeTimeseriesEnsemble(ensloc = ensloc,nstart = nstart, nend = nend,  variable = "baresoilFrac_lnd_mean")
  
   save(npp_ens_wave01,
        nbp_ens_wave01,
        cSoil_ens_wave01,
        cVeg_ens_wave01,
        lai_lnd_mean_ens_wave01,
        rh_lnd_sum_ens_wave01,
        fLuc_lnd_sum_ens_wave01,
        fHarvest_lnd_sum_ens_wave01,
        treeFrac_lnd_mean_ens_wave01,
        shrubFrac_lnd_mean_ens_wave01,
        baresoilFrac_lnd_mean_ens_wave01,
       file = "ensemble_timeseries_wave01.rdata" )
   
}

#total_land_carbon_ens_wave01 <- cSoil_ens_wave01 + cVeg_ens_wave01

lcol_wave0 <- makeTransparent('dodgerblue2',  120)
lcol_wave01 <- makeTransparent('firebrick',  120)
lcol_wave01_level2 <- 'gold'
stancol = 'black'

linePlotMultiEns <- function(years, ens1, ens2, ens3, col1, col2, col3, ylab, main, ylim = NULL){
  # Plot wave00 and wave01 timeseries on top of one another
  
  nt <- length(years) 
  if(is.null(ylim)){
    
  ylim = range(c(ens1[,1], ens1[,nt], ens2[,1], ens2[ ,nt], ens3[,1], ens3[, nt]))
  }
  
  else ylim <- ylim
  
  matplot(years, t(ens1), type = 'l', lty = 'solid',ylim = ylim, col = col1,
        ylab = ylab, main = main, xlab = '',
        bty = 'n')
  matlines(years, t(ens2), col = col2, lty = 'solid')
    matlines(years, t(ens3), col = col3, lty = 'solid')
}


par(mfrow= c(3,4), las = 1)

linePlotMultiEns(years = years, ens1 = npp_ens, ens2 = npp_ens_wave01, ens3 = npp_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'NPP')

lines(years,npp_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 =  nbp_ens, ens2 = nbp_ens_wave01, ens3 = nbp_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01,col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'NBP', ylim = c(-10,10))

lines(years, nbp_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = cSoil_ens, ens2 = cSoil_ens_wave01, ens3 = cSoil_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'cSoil', ylim = range(c(cSoil_ens[,1], cSoil_ens[,164])))

lines(years, cSoil_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = cVeg_ens, ens2 = cVeg_ens_wave01, ens3 = cVeg_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'cVeg')

lines(years, cVeg_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = lai_lnd_mean_ens, ens2 = lai_lnd_mean_ens_wave01, ens3 = lai_lnd_mean_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'Lai')

lines(years, lai_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = rh_lnd_sum_ens, ens2 = rh_lnd_sum_ens_wave01, ens3 = rh_lnd_sum_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01,  col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'RH')

lines(years, rh_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = fLuc_lnd_sum_ens, ens2 = fLuc_lnd_sum_ens_wave01, ens3 = fLuc_lnd_sum_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'fLuc')

lines(years, fLuc_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = fHarvest_lnd_sum_ens, ens2 = fHarvest_lnd_sum_ens_wave01,
                 ens3 = fHarvest_lnd_sum_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'fHarvest')

lines(years, fHarvest_lnd_sum_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = treeFrac_lnd_mean_ens, ens2 = treeFrac_lnd_mean_ens_wave01,
                 ens3 = treeFrac_lnd_mean_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'treefrac'
                 )

lines(years, treeFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = shrubFrac_lnd_mean_ens, ens2 = shrubFrac_lnd_mean_ens_wave01,
                 ens3 = shrubFrac_lnd_mean_ens[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'shrubfrac'
)

lines(years, shrubFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)

linePlotMultiEns(years = years, ens1 = baresoilFrac_lnd_mean_ens, ens2 = baresoilFrac_lnd_mean_ens_wave01,
                 ens3 = baresoilFrac_lnd_mean_ens_wave01[level2_ix_wave01, ],
                 col1 = lcol_wave0, col2 = lcol_wave01, col3 = lcol_wave01_level2,
                 ylab = 'GtC', main = 'baresoilfrac')

lines(years, baresoilFrac_lnd_mean_stan, col = stancol, lty = 'solid', lwd = 2)

reset()

legend('bottomright', legend = c('wave00','wave01','wave01 level2','standard'), lty = 'solid', lwd = 1.5, col = c(lcol_wave0, lcol_wave01, lcol_wave01_level2, stancol), inset = c(0.05, 0.15) )

NA
NA

This is a plot of timeseries of Wave00, Wave01, and level2-constrained wave01 on top of one another. We see that the wave01 is closer to the standard than wave00, and the level-2 constrained wave01 ensemble is often closer again. However, there are still quite large discrepancies. For example, baresoilfrac is often way too high, shrubfrac is often too low (though both these span the standard). Treefrac is away from zero, but still often too low or too high. While fHarvest looks good, fLuc does not appear constrained by the process at all. RH (soil respiration) looks well constrained, whereas lai is often too low.

One thing we could do next is constrain input space again, using observations or “tolerance to error” on some or all of these outputs.

We could also extend sensitivity analysis to work out what controls e.g. treefrac.

4 Emulator fits

We hope that running the new ensemble gives us a better emulator, and allows us to rule out more input space. We particularly hope that the emulator is better for those members that are inside AW’s constraints.

First, we can look at the emulator errors in two cases: The level1a data (a basic carbon cycle), and then with the Wave01 data, which should have similar characteristics. (We should have eliminated really bad simulations, but wave01 is not constrained the data perfectly to be within AW constraints.)

#fit_list_const_level1a <- createKmFitList(X = X_level1a, Y = Y_const_level1a_scaled)

Y_const_level1a_scaled_list <- mat2list(Y_const_level1a_scaled)

fit_list_const_level1a <- mclapply(X = Y_const_level1a_scaled_list, FUN = km, formula = ~., design = X_level1a,
                                   mc.cores = 4, control = list(trace = FALSE))

# Bind together the input matrices and scaled output data
X_level1a_wave01 <- rbind(X_level1a, X_wave01_train)[-440, ]

Y_const_level1a_wave01_scaled <- rbind(Y_const_level1a_scaled, Y_const_wave01_scaled)[-440, ]


apply(Y_const_level1a_wave01_scaled ,2, which.max)
     nbp_lnd_sum npp_nlim_lnd_sum    cSoil_lnd_sum     cVeg_lnd_sum 
             583              213              317              312 
apply(Y_const_level1a_wave01_scaled ,2, which.min)
     nbp_lnd_sum npp_nlim_lnd_sum    cSoil_lnd_sum     cVeg_lnd_sum 
             314              243              243              243 

Found the outlier - looks like it’s 440


findOutliers <- function(x, sds = 6){
  
  ix <- which(abs(x - mean(x)) > (sds * sd(x)))
  
}

apply(Y_const_level1a_wave01_scaled ,2, findOutliers, sds = 10)
integer(0)
# Create fit lists for the combined data
Y_const_level1a_wave01_scaled_list <- mat2list(Y_const_level1a_wave01_scaled)

fit_list_const_level1a_wave01 <- mclapply(X = Y_const_level1a_wave01_scaled_list , FUN = km, formula = ~., design = X_level1a_wave01,
                                   mc.cores = 4, control = list(trace = FALSE))

4.1 Leave-one-out analyses of emulator prediction accuracy


loolist_km_Y_level1a <- mclapply(X = fit_list_const_level1a, FUN = leaveOneOut.km, type = 'UK', trend.reestim = TRUE)

loolist_km_Y_level1a_wave01 <- mclapply(X = fit_list_const_level1a_wave01, FUN = leaveOneOut.km, type = 'UK', trend.reestim = TRUE)

loostats_km_Y_level1a <- lapply(fit_list_const_level1a, FUN = kmLooStats)
loostats_km_Y_level1a_wave01 <- lapply(fit_list_const_level1a_wave01, FUN = kmLooStats)

The top row shows the leave-one-out prediction accuracy of the original wave00 ensemble, and the lower row the entire wave00 AND wave01 ensemble combined.


#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(2,4), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a)){
  
  y <- Y_const_level1a_scaled[, i]
  loo <- loolist_km_Y_level1a[[i]]
  ylim <- range(c(loo$mean - (2*loo$sd), loo$mean + (2*loo$sd)) )
  plot(y, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave00col, 100),
       pch = 19)
  segments(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave00col, 50))
  abline(0,1)
  legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2  )
  legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Level 1a ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)

#dev.off()

#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
for(i in 1:length(loolist_km_Y_level1a)){
  
  y <- Y_const_level1a_wave01_scaled[, i]
  loo <- loolist_km_Y_level1a_wave01[[i]]
  ylim <- range(c(loo$mean - (2*loo$sd), loo$mean + (2*loo$sd)) )
  plot(y, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave01col, 100),
       pch = 19)
  segments(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave01col, 100))
  abline(0,1)
  legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2  )
  legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a_wave01[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Level 1a ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)

# remove to level 1a Relative to toplevel_ix - useful for plotting etc.
#toplevel_to_level_1a_ix <-(toplevel_ix[-Y_nlevel0_ix])[level1a_ix]

# So further constraining to level 2 can be associated back to the top level.

level2_ix <- which(Y_const_level1a_scaled[,'nbp_lnd_sum'] > 0 &
                    Y_const_level1a_scaled[,'npp_nlim_lnd_sum'] > 35 &  Y_const_level1a_scaled[,'npp_nlim_lnd_sum'] < 80 &
                    Y_const_level1a_scaled[,'cSoil_lnd_sum'] > 750 & Y_const_level1a_scaled[,'cSoil_lnd_sum'] < 3000 &
                  Y_const_level1a_scaled[,'cVeg_lnd_sum'] > 300 & Y_const_level1a_scaled[,'cVeg_lnd_sum'] < 800
                  
  )

level2_ix_level1a_wave01 <- which(Y_const_level1a_wave01_scaled[,'nbp_lnd_sum'] > 0 &
                    Y_const_level1a_wave01_scaled[,'npp_nlim_lnd_sum'] > 35 & Y_const_level1a_wave01_scaled[,'npp_nlim_lnd_sum'] < 80 &
                    Y_const_level1a_wave01_scaled[,'cSoil_lnd_sum'] > 750 & Y_const_level1a_wave01_scaled[,'cSoil_lnd_sum'] < 3000 &
                  Y_const_level1a_wave01_scaled[,'cVeg_lnd_sum'] > 300 & Y_const_level1a_wave01_scaled[,'cVeg_lnd_sum'] < 800
                  )

4.2 Emulator accuracy of members from wave 00 and wave 01 that pass level 2 (AW’s) constraints

We see that the error stats for some of the outputs from wave01 are worse, but there are many more ensemble members that lie within the constraints for wave 01.

“pmae” is “proportional mean absolue error”, which is the mean absolute error expressed as a percentage of the original (minimally constrained) ensemble range in that output.


#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(2,4), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a)){
  
  y <- Y_const_level1a_scaled[level2_ix, i]
  loo <- loolist_km_Y_level1a[[i]]
  ylim <- range(c(loo$mean[level2_ix] - (2*loo$sd[level2_ix]), loo$mean[level2_ix] + (2*loo$sd[level2_ix])) )
  plot(y, loo$mean[level2_ix], xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave00col, 100),
       pch = 19)
  segments(x0 = y, y0 = loo$mean[level2_ix] - (2*loo$sd[level2_ix])  , x1 = y , y1 = loo$mean[level2_ix] + (2*loo$sd[level2_ix]), col = makeTransparent(wave00col, 100))
  abline(0,1)
  legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2  )
  legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)

}

#dev.off()

#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
for(i in 1:length(loolist_km_Y_level1a)){
  
  y <- Y_const_level1a_wave01_scaled[level2_ix_level1a_wave01, i]
  loo <- loolist_km_Y_level1a_wave01[[i]]
  ylim <- range(c(loo$mean[level2_ix_level1a_wave01] - (2*loo$sd[level2_ix_level1a_wave01]), loo$mean[level2_ix_level1a_wave01] + (2*loo$sd[level2_ix_level1a_wave01])) )
  plot(y, loo$mean[level2_ix_level1a_wave01], xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave01col, 100),
       pch = 19)
  segments(x0 = y, y0 = loo$mean[level2_ix_level1a_wave01] - (2*loo$sd[level2_ix_level1a_wave01])  , x1 = y , y1 = loo$mean[level2_ix_level1a_wave01] + (2*loo$sd[level2_ix_level1a_wave01]), col = makeTransparent(wave01col, 50))
  abline(0,1)
  legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2  )
  legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a_wave01[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Level 2 constrained ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)

4.3 Does the emulator improve is you look at only the 37 members that pass level 2 constraints in wave 00?

This gives us an idea of how good the emulator is where it really matters, and as the members are consistent, gives us a fairer idea of whether the emulators have improved with more members.

Good news is, the emulators are more accurate for wave01.



kmLooStatsSubset <- function (km, ix, type = "UK") 
{
  # Calculate summary statistics for a subset of the members of a km fit list
    loo <- leaveOneOut.km(km, type = type, trend.reestim = TRUE)
    preddiff <- loo$mean[ix] - km@y[ix]
    mae <- mean(abs(preddiff))
    rmse <- sqrt(mean(preddiff^2))
    maxerr <- max(preddiff)
    absdiff <- abs(diff(range(km@y)))
    pmae <- (mae/absdiff) * 100
    return(list(loo = loo, mae = mae, pmae = pmae, maxerr = maxerr))
}

loolist_km_Y_level1a_level2 <- rapply(loolist_km_Y_level1a, f = function(x) x[level2_ix], how = "list")

loolist_km_Y_level1a_wave01_level2 <- rapply(loolist_km_Y_level1a_wave01, f = function(x) x[level2_ix], how = "list")

#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(2,2), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a_level2)){
  
  y <- Y_const_level1a_scaled[level2_ix, i]
  
  loo <- loolist_km_Y_level1a_level2[[i]]
  ylim <- range(c(loo$mean- (2*loo$sd), loo$mean + (2*loo$sd)) )
  plot(y, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave00col, 250),
       pch = 19)
  arrows(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave00col, 150) ,  angle = 90, code = 3, length = 0.03)
  
  y1 <- Y_const_level1a_wave01_scaled[level2_ix, i]
  loo <- loolist_km_Y_level1a_wave01_level2[[i]]
  
    points(y1, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent(wave01col, 250),
       pch = 19)
  arrows(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent(wave01col, 250),  angle = 90, code = 3, length = 0.03)
  
  
  abline(0,1)
  legend('topleft', legend = colnames(Y_const_level1a_scaled)[i], bty = 'n', text.font = 2  )
  legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Level 2 wave 00 ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)

reset()
legend('topleft', pch = 19, legend = c('wave00', 'wave01'), col = c(wave00col, wave01col ), horiz = TRUE)

NA
NA

These leave-one-out prediction accuracy plots rank the ensemble members from largest underprediction to largest overprediction using the wave00 predictions. A perfect prediction would appear on the horizontal “zero” line.

Many of the wave01 predictions are closer to the horizontal line, and therefore more accurate predictions.

None of the predictions are outside the uncertainty bounds, which suggests they are overconservative (should be smaller).


#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(4,1), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a_level2)){
  
  y <- Y_const_level1a_scaled[level2_ix, i]

  loo_00 <- loolist_km_Y_level1a_level2[[i]]
  loo_01 <- loolist_km_Y_level1a_wave01_level2[[i]]
  
  preddiff_wave00 <- y - loo_00$mean
  preddiff_wave01 <- y - loo_01$mean
  
    # rank by the original wave 00 predictions
  loo_rank_ix <- sort(preddiff_wave00 , index.return = TRUE)
  
   ylim <- range(c(preddiff_wave00[loo_rank_ix$ix] - (2*loo_00$sd[loo_rank_ix$ix]),
                   preddiff_wave00[loo_rank_ix$ix] + (2*loo_00$sd[loo_rank_ix$ix]),
                   preddiff_wave01[loo_rank_ix$ix] - (2*loo_01$sd[loo_rank_ix$ix]),
                   preddiff_wave01[loo_rank_ix$ix] + (2*loo_01$sd[loo_rank_ix$ix])
                   )
                 )
   
   plot(preddiff_wave00[loo_rank_ix$ix], xlab = '', ylab = '', main = '' , col = makeTransparent(wave00col, 255),
        pch = 19, ylim = ylim)
   
   abline(h = 0)
   
  arrows(x0 = 1:length(y), y0 = preddiff_wave00[loo_rank_ix$ix] - (2*loo_00$sd[loo_rank_ix$ix])  , x1 = 1:length(y) , y1 = preddiff_wave00[loo_rank_ix$ix] + (2*loo_00$sd[loo_rank_ix$ix]), col = makeTransparent(wave00col, 150),  angle = 90, code = 3, length = 0.03)
   
  points(preddiff_wave01[loo_rank_ix$ix], xlab = '', ylab = '', main = '' , col = makeTransparent(wave01col, 255),
        pch = 19)
  
    arrows(x0 = 1:length(y), y0 = preddiff_wave01[loo_rank_ix$ix] - (2*loo_01$sd[loo_rank_ix$ix])  , x1 = 1:length(y) , y1 = preddiff_wave01[loo_rank_ix$ix] + (2*loo_01$sd[loo_rank_ix$ix]), col = makeTransparent(wave01col, 150), angle = 90, code = 3, length = 0.03)
   
   mtext(colnames(Y_const_level1a_scaled)[i], side = 3, adj = 0, line = 1)
  

}


 reset()
 legend('topleft', pch = 19, legend = c('wave00', 'wave01'), col = c(wave00col, wave01col ), horiz = TRUE)


loostats_km_Y_level1a_sub <- lapply(fit_list_const_level1a, FUN = kmLooStatsSubset, ix = level2_ix)
loostats_km_Y_level1a_wave01_sub <- lapply(fit_list_const_level1a_wave01, FUN = kmLooStatsSubset, ix = level2_ix)

Looking at the proportional mean absolute error (pmae), expressed in percent, we can see that it doesn’t improve much for the whole ensemble, but does improve significantly for the subset of ensemble members that fall within AW’s constraints from the first ensemble (marked "_sub").


pmae_wave00 <- lapply(loostats_km_Y_level1a, FUN = function(x) x$pmae )
pmae_wave01 <- lapply(loostats_km_Y_level1a_wave01, FUN = function(x) x$pmae )

pmae_wave00_sub <- lapply(loostats_km_Y_level1a_sub, FUN = function(x) x$pmae )
pmae_wave01_sub <- lapply(loostats_km_Y_level1a_wave01_sub, FUN = function(x) x$pmae )

pmae_table <- cbind(pmae_wave00, pmae_wave01, pmae_wave00_sub, pmae_wave01_sub)

print(pmae_table)
     pmae_wave00 pmae_wave01 pmae_wave00_sub pmae_wave01_sub
[1,] 5.084153    4.927634    7.17902         4.913062       
[2,] 4.282042    4.007528    4.804363        4.085545       
[3,] 3.597314    3.790255    4.555892        3.833982       
[4,] 4.240908    4.515949    4.814           3.226669       

5 Comparing atmospheric growth in wave00, wave01 and observations


obscol = 'purple'
wave01_level2col <- 'gold'

ag <- matrix(nrow = nrow(nbp_ens), ncol = ncol(nbp_ens))

ag01 <- matrix(nrow = nrow(nbp_ens_wave01), ncol = ncol(nbp_ens_wave01))

for(i in 1:nrow(nbp_ens)){
  
ag[i, ] <- historical_carbon_budget$`fossil emissions excluding carbonation`[resid_ix] - historical_carbon_budget$`ocean sink`[resid_ix] -  nbp_ens[i, ]
  
}


for(i in 1:nrow(nbp_ens_wave01)){
ag01[i, ] <- historical_carbon_budget$`fossil emissions excluding carbonation`[resid_ix] - historical_carbon_budget$`ocean sink`[resid_ix] -  nbp_ens_wave01[i, ]
  
}

ag_stan <- historical_carbon_budget$`fossil emissions excluding carbonation`[resid_ix] - historical_carbon_budget$`ocean sink`[resid_ix] -  nbp_stan

#pdf(width = 8, height = 6, file = 'ag.pdf')
matplot(years, t(ag), type = 'l', lty = 'solid',ylim = c(-2, 10), col = makeTransparent(wave00col,100), 
         ylab = 'GtC', main = 'atmospheric growth', xlab = '',
         bty = 'n', xlim = c(1960, 2013))

matlines(years, t(ag01), col = makeTransparent(wave01col,100), lty = 'solid')

matlines(years, t(ag01[level2_ix_wave01, ]), col = makeTransparent(wave01_level2col, 100) , lty = 'solid')

lines(historical_carbon_budget$Year, historical_carbon_budget$`atmospheric growth`, col = obscol, lwd =2)

lines(years, ag_stan, col = stancol, lwd =2)


legend('topleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard', 'GCB'), col = c(wave00col, wave01col, wave01_level2col, stancol, obscol), lty = 'solid', lwd = 2)

#dev.off()

5.1 Andy asks - what constraint does that give us in cumulative NBP?


cumulative_nbp_ens <- t(apply(nbp_ens, 1, cumsum))

cumulative_nbp_ens_wave01 <- t(apply(nbp_ens_wave01, 1, cumsum))

cumulative_nbp_stan <- cumsum(nbp_stan)

matplot(years, t(cumulative_nbp_ens), type = 'l', lty = 'solid',ylim = c(-130, 250), col = makeTransparent(wave00col,150), 
         ylab = 'GtC', main = 'cumulative NBP', xlab = '',
         bty = 'n', xlim = c(1850, 2020))

matlines(years, t(cumulative_nbp_ens_wave01),
         col = makeTransparent(wave01col,150),
         lty = 'solid')

matlines(years, t(cumulative_nbp_ens_wave01[level2_ix_wave01, ]),
         col = makeTransparent(wave01_level2col, 150),
         lty = 'solid')


lines(years, cumulative_nbp_stan, col = makeTransparent(stancol, 200), lty = 'solid', lwd = 1.5 )


legend('topleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), lty = 'solid', lwd = 1.5)

5.2 Eddy suggests measuring cumulative NBP against atmospheric growth rate


# Cumulative NBP at the end of the run
cnbp_modern_ens <- apply(cumulative_nbp_ens[, 135:164], 1, mean)

cnbp_modern_wave01 <- apply(cumulative_nbp_ens_wave01[, 135:164], 1, mean)

cnbp_modern_stan <- mean(cumulative_nbp_stan[135:164])

Calculate the atmospheric growth rate of 1984- 2013 using a simple linear fit

# agr = atmospheric growth rate
# agiav = atmospheric growth interannual variability

agr_modern_ens <- rep(NA, length = nrow(ag))
agiav_modern_ens <- rep(NA, length = nrow(ag))

for(i in 1:nrow(ag)){
  
  dat <- data.frame(t =1:30, ag =  ag[i, 135:164])
  fit <- lm(ag ~ t, data = dat)
  agr_modern_ens[i] <- coef(fit)['t']
  agiav_modern_ens[i] <- sd(fit$residuals)
}


agr_modern_wave01 <- rep(NA, length = nrow(ag01))
agiav_modern_wave01 <- rep(NA, length = nrow(ag01))

for(i in 1:nrow(ag01)){
  
  dat <- data.frame(t =1:30, ag =  ag01[i, 135:164])
  fit <- lm(ag ~ t, data = dat)
  agr_modern_wave01[i] <- coef(fit)['t']
  agiav_modern_wave01[i] <- sd(fit$residuals)
}

dat <- data.frame(t =1:30, ag =  ag_stan[135:164])
agr_stan_fit <- lm(ag ~ t, data = dat) 
agr_stan <- coef(agr_stan_fit)['t']
agiav_stan <- sd(agr_stan_fit$residuals)
  
  

plot(agr_modern_ens, cnbp_modern_ens,  col = makeTransparent(wave00col,150),
     ylim = c(-120,220), pch = 19,
     xlab = 'Atmospheric growth rate 1984-2014',
     ylab = 'Cumulative NBP from preindustrial by 1984-2013'
     )

cnbp_wave01_cols <- rep(wave01col, length(cnbp_modern_wave01))
cnbp_wave01_cols[level2_ix_wave01] <- wave01_level2col 

points( agr_modern_wave01,cnbp_modern_wave01, col = makeTransparent(cnbp_wave01_cols, 150), pch = 19)
points( agr_stan,cnbp_modern_stan, col = stancol, pch = 19, cex = 1.5)

legend('topleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), pch = 19)


print(c('correlation agr vs cnbp (all members)', cor(agr_modern_ens, cnbp_modern_ens)))
[1] "correlation agr vs cnbp (all members)" "-0.0407011946805412"                  
print(c('correlation agr vs cnbp (wave01)', cor(agr_modern_wave01, cnbp_modern_wave01)))
[1] "correlation agr vs cnbp (wave01)" "0.00334447281747124"             
fit_wave00 <- lm(agr_modern_ens ~ cnbp_modern_ens)
fit_wave01 <- lm(agr_modern_wave01 ~ cnbp_modern_wave01)

print(summary(fit_wave00))

Call:
lm(formula = agr_modern_ens ~ cnbp_modern_ens)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.047413 -0.003802  0.003838  0.006715  0.023090 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)      1.114e-01  5.176e-04 215.173   <2e-16 ***
cnbp_modern_ens -1.379e-09  1.519e-09  -0.908    0.364    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.01155 on 497 degrees of freedom
Multiple R-squared:  0.001657,  Adjusted R-squared:  -0.0003522 
F-statistic: 0.8247 on 1 and 497 DF,  p-value: 0.3643
print(summary(fit_wave01))

Call:
lm(formula = agr_modern_wave01 ~ cnbp_modern_wave01)

Residuals:
    Min      1Q  Median      3Q     Max 
  -4639   -4639   -4639   -4639 1382335 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)
(Intercept)        4.639e+03  4.639e+03   1.000    0.318
cnbp_modern_wave01 5.475e-29  9.483e-28   0.058    0.954

Residual standard error: 80210 on 298 degrees of freedom
Multiple R-squared:  1.119e-05, Adjusted R-squared:  -0.003344 
F-statistic: 0.003333 on 1 and 298 DF,  p-value: 0.954

Interannual variability and cumulative NBP

(correlations are close to zero, especially in the later wave)


plot(agiav_modern_ens, cnbp_modern_ens,  col = makeTransparent(wave00col,150),
     ylim = c(-120,220), pch = 19,
     xlab = 'Atmospheric growth IAV 1984-2014',
     ylab = 'Cumulative NBP from preindustrial by 1984-2013'
     )

points( agiav_modern_wave01, cnbp_modern_wave01, col = makeTransparent(cnbp_wave01_cols, 150), pch = 19)
points( agiav_stan,cnbp_modern_stan, col = stancol, pch = 19, cex = 1.5)

legend('bottomright', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), pch = 19)



print(cor(agiav_modern_ens, cnbp_modern_ens))
[1] 0.03223847
print(cor( agiav_modern_wave01, cnbp_modern_wave01))
[1] 0.003344484

5.3 How close can we get the model to reality?

Using Atmospheric Growth Rate as an example, how close can we get the model to observations? Can we do better than standard? What are the trade offs of doing so? How does getting close in AGR affect performance in other outputs?

# Define the observed atmospheric growth rate.

ag_obs <- historical_carbon_budget$`atmospheric growth`[which(historical_carbon_budget$Year %in% years)]

# Model departure from observations of atmospheric growth
ag_err <- sweep(ag, 2, ag_obs, FUN = '-')
ag01_err <- sweep(ag01, 2, ag_obs, FUN = '-')


long_modern_years <- 1960:2013
ag_modern_ix <- which( years %in% long_modern_years)

ag_err_modern <- ag_err[, ag_modern_ix]
ag01_err_modern <- ag01_err[, ag_modern_ix]


ag_err_stan <- ag_stan - ag_obs
ag_err_stan_modern <- ag_err_stan[ag_modern_ix]
matplot(long_modern_years, t(ag_err_modern), type = 'l', lty = 'solid', col = makeTransparent(wave00col, 100), main = 'Amospheric Growth Error')

errcol <- rep(wave01col, nrow(ag01))
#errcol[level2_ix_wave01] <- wave01_level2col
matlines(long_modern_years, t(ag01_err_modern), col = makeTransparent(errcol, 100), lty = 'solid')
matlines(long_modern_years, t(ag01_err_modern[level2_ix_wave01, ]), col = makeTransparent(wave01_level2col,100), lty = 'solid')

lines(long_modern_years, ag_err_stan_modern, col = stancol)

legend('bottomleft', legend = c('wave00', 'wave01', 'wave01 constrained', 'standard'), col = c(wave00col, wave01col, wave01_level2col, stancol), lty = 'solid', lwd = 1.5)

abline(h=0)


# Atmospheric growth mean error
ag_modern_me <- apply(ag_err_modern,1,mean)
ag01_modern_me <- apply(ag01_err_modern,1, mean)
ag_modern_me_stan <- mean(ag_err_stan[ag_modern_ix])


# Mean absolute error
ag_modern_mae <- apply(abs(ag_err_modern),1,mean)
ag01_modern_mae <- apply(abs(ag01_err_modern),1, mean)
ag_modern_mae_stan <- mean(abs(ag_err_stan[ag_modern_ix]))



# Root mean square error
ag_modern_rmse <- apply(ag_err_modern,1, function(x) sqrt(mean(x^2)))
ag01_modern_rmse <- apply(ag01_err_modern,1, function(x) sqrt(mean(x^2)))
ag_modern_rmse_stan <- sqrt(mean(ag_err_stan[ag_modern_ix]^2))

# There are some big outliers 
outlier_ix_wave01 <- which(abs(ag01_modern_me )> 100)

par(mfrow = c(2,1))
hist(ag_modern_me, main = 'Atmospheric growth mean error', col = wave00col, xlim = c(-3, 3))
rug(ag_modern_me_stan, col = stancol, lwd = 2)
hist(ag01_modern_me[-outlier_ix_wave01], col = wave01col, xlim = c(-3,3), main = '')
rug(ag_modern_me_stan, col = stancol, lwd = 2)

par(mfrow = c(2,1))

hist(ag_modern_mae, main = 'Atmospheric growth mean absolute error', col = wave00col, xlim = c(0, 3))
rug(ag_modern_mae_stan, col = stancol, lwd = 2)
hist(ag01_modern_mae[-outlier_ix_wave01], col = wave01col, lwd = 2, xlim = c(0,3), main = '')
rug(ag_modern_mae_stan, col = stancol, lwd = 2)

par(mfrow = c(2,1))

hist(ag_modern_rmse, xlim = c(0,3), col = wave00col, main = 'Atmospheric growth RMSE')
rug(ag_modern_rmse_stan, col = stancol, lwd = 2 )
hist(ag01_modern_rmse[-outlier_ix_wave01], col = wave01col, xlim = c(0,3), main = '')
rug(ag_modern_rmse_stan, col = stancol, lwd = 2 )

NA
NA
NA

We’ve established that most of the original ensemble have an ME/MAE/RMSE larger than the standard run. More (but few) of the wave01 perform better than standard.



better_ix_ag_rmse <- which(ag_modern_rmse < ag_modern_rmse_stan)
better_ix_ag01_rmse <- which(ag01_modern_rmse < ag_modern_rmse_stan)


X_better_ag <- rbind(X[better_ix_ag_rmse, ], X_wave01_train[better_ix_ag01_rmse, ])

A map of the 2D projections of parameter space where the ensemble member performs better than standard.

The blue part is the first wave, and not subject to constraint so may be removed in the second wave (wave01).


better_ix <- 1:nrow(X_better_ag)

better_cols <- c(rep(wave00col, length(better_ix_ag_rmse)), rep(wave01col,length(better_ix_ag01_rmse)))

pairs(X_better_ag,
      col = better_cols,
      gap = 0,
      xlim = c(0,1), ylim = c(0,1),
      pch = 19,
      cex= 0.8,
      lower.panel = NULL)

NA
NA
NA

5.4 Build emulators and find parts of parameter space that to better than standard at atmospheric growth.

Having trouble fitting RMSE, to trying mean error.

Why is there an odd collection at just under 1?

# there are 100 wave01 ensemble members that pass the level 2 constrainst
length(level2_ix_wave01)
[1] 100
better_ix_ag_me <- which(abs(ag_modern_me) < abs(ag_modern_me_stan))

# There are 104 that have a smaller mean error than standard
better_ix_ag01_me <- which(abs(ag01_modern_me) < abs(ag_modern_me_stan))

# There are only 41 that pass level 2 AND have smaller error than standard
level2_and_better_ag01_ix <- intersect(level2_ix_wave01, better_ix_ag01_me)

This next pairs plot looks at all the ensemble members that have a better mean atmospheric growth error than standard.

X_better_ag <- rbind(X[better_ix_ag_me, ], X_wave01_train[better_ix_ag01_me, ])

better_ix <- 1:nrow(X_better_ag)

better_cols <- c(makeTransparent(rep(wave00col, length(better_ix_ag_me)), 100), makeTransparent(rep(wave01col,length(better_ix_ag01_me)), 100))

pairs(X_better_ag,
      col = better_cols,
      gap = 0,
      xlim = c(0,1), ylim = c(0,1),
      pch = 19,
      cex= 0.8,
      lower.panel = NULL)

This next plot looks at all the ensemble members that have a better mean atmospheric growth error than standard AND pass the level 2 constraints.

The number is small (41/300), but the ensemble members seem spread across parameter space.


X_level2_and_better_ag01 <- X[level2_and_better_ag01_ix, ]
pairs(X_level2_and_better_ag01,
      col = makeTransparent('black', 150),
      gap = 0,
      xlim = c(0,1), ylim = c(0,1),
      pch = 19,
      cex= 0.8,
      lower.panel = NULL)


plot(fit_ag_me)

NA
NA
NA
NA
(length(X_kept_ix) / nunif) * 100
[1] 21.057

5.5 Input space with low Atmospheric Growth Error

This pairs plot shows the 2d and marginal density of emulated input points where the emulated atmospheric growth is closer to the observations than the standard model.

This technique might provide a useful set of points for optimising the model (at least to atmospheric growth).

#pairs(X_unif[X_kept_ix, ][1:50,],
#      col = makeTransparent('black',50),
#      gap = 0,
#      xlim = c(0,1), ylim = c(0,1),
#      pch = 19,
#      cex= 0.8,
#      lower.panel = NULL)

par(oma = c(0,0,0,3), bg = 'white')


panel.hist = function(x, ...) {
  usr = par("usr"); on.exit(par(usr))
  par(usr = c(usr[1:2], 0, 1.5))
  hist(x, freq = FALSE, col="cyan", add=TRUE) 
  lines(density(x))
}

pairs(X_unif[X_kept_ix, ],
    #  labels = 1:d,
      gap = 0, lower.panel = NULL, xlim = c(0,1), ylim = c(0,1),
      panel = dfunc_up,
      diag.panel = panel.hist,
      cex.labels = 1,
      col.axis = 'white',
      dfunc_col = rb)

Next, check emulators of all the other outputs and apply the constraints to them. See how the constraints change.



# First, try the emulators built using just wave01
#fit_list_const_level1a


Y_const_pred_unif_mean <- matrix(NA, ncol = ncol(Y_const_level1a_scaled), nrow = nrow(X_unif))
colnames(Y_const_pred_unif_mean) <- colnames(Y_const_level1a_scaled)

Y_const_pred_unif_sd <- matrix(NA, ncol = ncol(Y_const_level1a_scaled), nrow = nrow(X_unif)) 
colnames(Y_const_pred_unif_sd) <- colnames(Y_const_level1a_scaled)

for(i in 1:length(fit_list_const_level1a)){
  
  pred_unif <- predict.km(object=fit_list_const_level1a[[i]], newdata = X_unif, type = 'UK')
  
  Y_const_pred_unif_mean[,i ] <- pred_unif$mean
  Y_const_pred_unif_sd[,i ] <- pred_unif$sd
}

5.6 Input space with emulated members passing Level 2 constraints.

(length(level2_ix_em) / nunif) * 100
[1] 12.985

5.7 Input space with emulated members passing Level 2 constraints AND low atmospheric growth error

Emulated members passing level2 constraints AND having lower error in atmospheric growth than standard.

Red point indicates the standard input.

The position of the standard input with regards to the histograms give us an idea of what we might do to improve the simulation - at least in terms of atmospheric growth. If we were to move the input towards areas of higher density, we might expect a better model performance.


X_stan_norm <- normalize(matrix(rep(1, 32), nrow = 1), wrt = lhs)

level2_and_ag_ix_em <- which(Y_const_pred_unif_mean[,'nbp_lnd_sum'] > 0 &
                    Y_const_pred_unif_mean[,'npp_nlim_lnd_sum'] > 35 & Y_const_pred_unif_mean[,'npp_nlim_lnd_sum'] < 80 &
                    Y_const_pred_unif_mean[,'cSoil_lnd_sum'] > 750 & Y_const_pred_unif_mean[,'cSoil_lnd_sum'] < 3000 &
                  Y_const_pred_unif_mean[,'cVeg_lnd_sum'] > 300 & Y_const_pred_unif_mean[,'cVeg_lnd_sum'] < 800 &
                    abs(pred_unif_ag$mean) < abs(ag_modern_me_stan)
                  )



pairs(rbind(X_unif[level2_and_ag_ix_em, ], X_stan_norm),
    #  labels = 1:d,
      gap = 0, lower.panel = NULL, xlim = c(0,1), ylim = c(0,1),
      panel = dfunc_up_truth,
      diag.panel = panel.hist,
      cex.labels = 1,
      col.axis = 'white',
      dfunc_col = rb)



(length(level2_and_ag_ix_em) / nunif) * 100
[1] 5.446
LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgSlVMRVMxcDAgZW5zZW1ibGUgd2F2ZTAxIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwotLS0KCiMgSW50cm9kdWN0aW9uCgpBIGNvbXBhcmlzb24gb2YgSlVMRVMtRVMtMXAwIHdhdmUwMSBtZW1iZXJzIGFnYWluc3QgdGhlIG9yaWdpbmFsIGVuc2VtYmxlICh3YXZlMDApLgoKV2F2ZSAwMSBpbnB1dCBwYXJhbWV0ZXIgc2V0cyB3ZXJlIHBpY2tlZCB1c2luZyBIaXN0b3J5IG1hdGNoaW5nIHRvIGZhbGwgd2l0aGluIEFuZHkgV2lsdHNoaXJlJ3MgYmFzaWMgY29uc3RyYWludHMgb24gTkJQLCBOUFAsIGNTb2lsIGFuZCBjVmVnIHN0b2NrcyBhdCB0aGUgZW5kIG9mIHRoZSAyMHRoIGNlbnR1cnkuIFdlIHVzZSAzMDAgb2YgdGhlIDUwMCBtZW1iZXJzLCBrZWVwaW5nIGJhY2sgMi81dGhzIGZvciBlbXVsYXRvciB2YWxpZGF0aW9uIGxhdGVyLgoKV2UgYW5zd2VyIHNvbWUgYmFzaWMgcXVlc3Rpb25zLgoKV2hhdCBwcm9wb3J0aW9uIG9mIHRoZSBuZXcgZW5zZW1ibGUgbWF0Y2ggQVcncyBjb25zdHJhaW50cz8KCldoYXQgZG8gdGhlIHRpbWVzZXJpZXMgb2YgY2FyYm9uIGN5Y2xlIHByb3BlcnRpZXMgbG9vayBsaWtlIHdpdGggYW5kIHdpdGhvdXQgQVcncyBjb25zdHJhaW50cz8KCkhvdyBnb29kIGlzIGEgR1AgZW11bGF0b3I/IERvZXMgaXQgZ2V0IGJldHRlciBvdmVyYWxsIHdpdGggdGhlIG5ldyBlbnNlbWJsZSBtZW1iZXJzIGFkZGVkPyBJbiBwYXJ0aWN1bGFyLCBkb2VzIGl0IGdldCBiZXR0ZXIgZm9yIHRob3NlIG1lbWJlcnMgd2l0aGluIHRoZSBBVyBjb25zdHJhaW50cz8KCkRvZXMgY29tcGFyaXNvbiBvZiB0aGUgZW5zZW1ibGUgd2l0aCBBdG1vc3BoZXJpYyBncm93dGggb2JzZXJ2YXRpb25zIGdpdmUgbW9yZSBvZiBhIGNvbnN0cmFpbnQ/CgpUbyBkbzoKCkRvZXMgdGhlIHNlbnNpdGl2aXR5IGFuYWx5c2lzIGNoYW5nZT8KCiMgUHJlbGltaW5hcmllcwpMb2FkIGxpYnJhcmllcywgZnVuY3Rpb25zIGFuZCBkYXRhLgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzID0gJ2hpZGUnfQojIExvYWQgaGVscGVyIGZ1bmN0aW9ucwoKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5wYXRoID0gImZpZ3MvIiwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmdzID0gRkFMU0UpCgojIGxvYWQgaGVscGVyIGZ1bmN0aW9ucywgZGF0YSBhbmQgZG8gcHJlbGltaW5hcnkgcHJvY2Vzc2luZyBvZiB0aGUgZW5zZW1ibGUuCnNvdXJjZSgnSlVMRVMtRVMtMXAwLWNvbW1vbi5SJykKCmBgYAoKYGBge3J9CgplbnNsb2MgPC0gJy9zY3JhdGNoL2hhZGF3L2p1bGVzX3Bvc3Rwcm9jZXNzL3UtY2swMDYvJwoKYGBgCgoKYGBge3J9CgptYWtlSnVsZXNFbnNlbWJsZU1vZGVyblZhbHVlIDwtIGZ1bmN0aW9uKGVuc2xvYywgdmFybGlzdCwgbnN0YXJ0LCBuZW5kLCBpeCA9IDE0NDoxNjQpewogIAogIG5lbnMgPC0gKG5lbmQgLSBuc3RhcnQpICsgMQogIGRhdG1hdCA8LSBtYXRyaXgobnJvdyA9IG5lbnMsIG5jb2wgPSBsZW5ndGgodmFybGlzdCkpCiAgY29sbmFtZXMoZGF0bWF0KSA8LSB2YXJsaXN0CiAgCiAgZW5zbGlzdCA8LSBwYXN0ZSgiUCIsIGZvcm1hdEMobnN0YXJ0Om5lbmQsIHdpZHRoPTQsIGZsYWc9IjAiKSwgc2VwPSIiKQogIAogIGZvcihpIGluIDE6bmVucyl7CiAgICAKICAgIHZlYyA8LSByZXAoTkEsIGxlbmd0aCh2YXJsaXN0KSkKICAgIAogICAgZW5zbWVtYmVyIDwtIGVuc2xpc3RbaV0gCiAgICAKICAgIGZuIDwtIHBhc3RlMChlbnNsb2MsZW5zbWVtYmVyLCcvc3RhdHMvJywnSlVMRVMtRVMtMXAwXycsZW5zbWVtYmVyLCdfQW5udWFsX2dsb2JhbC5uYycpCiAgICAKICAgIHRyeShuYyA8LSBuY19vcGVuKHBhc3RlMChmbikpKQogICAgdHJ5KHZlYyA8LSBzYXBwbHkodmFybGlzdCwgRlVOID0gbW9kZXJuVmFsdWUsIG5jID0gbmMsIGl4ID0gaXgpKQogICAgZGF0bWF0W2ksIF0gPC0gdmVjCiAgICBuY19jbG9zZShuYykKICB9CiAgcmV0dXJuKGxpc3QoZGF0bWF0ID0gZGF0bWF0LCBlbnNsaXN0ID0gZW5zbGlzdCkpCn0KICAKYGBgCgoKYGBge3J9Cm5zdGFydCA8LSA0OTkKbmVuZCA8LSA3OTgKCiNpZiAoZmlsZS5leGlzdHMoImVuc2VtYmxlX3dhdmUwMS5yZGF0YSIpKSB7CiMgIGxvYWQoImVuc2VtYmxlX3dhdmUwMS5yZGF0YSIpCiN9IGVsc2UgewogIAplbnNfd2F2ZTAxX212IDwtIG1ha2VKdWxlc0Vuc2VtYmxlTW9kZXJuVmFsdWUoZW5zbG9jID0gJy9zY3JhdGNoL2hhZGF3L2p1bGVzX3Bvc3Rwcm9jZXNzL3UtY2swMDYvJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJsaXN0ID0geV9uYW1lc19zdW0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc3RhcnQgPSBuc3RhcnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZW5kID0gbmVuZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpeCA9IDE0NDoxNjQpIAogIAojICBzYXZlKGVuc193YXZlMDFfbXYsIGZpbGUgPSJlbnNlbWJsZV93YXZlMDEucmRhdGEiKQojfQogIApgYGAKCmBgYHtyfQoKbGhzX3dhdmUwMSA8LSByZWFkLnRhYmxlKCAnLi4vY29uZl9maWxlc19hdWdtZW50X0pVTEVTLUVTLTFwMC9saHNfZXhhbXBsZS50eHQnLCBoZWFkZXIgPSBUUlVFKQoKWF93YXZlMDEgPSBub3JtYWxpemUobGhzX3dhdmUwMSwgd3J0ID0gcmJpbmQobGhzX2ksIGxoc19paSwgbGhzX3dhdmUwMSkpCmNvbG5hbWVzKFhfd2F2ZTAxKSA9IGNvbG5hbWVzKGxoc193YXZlMDEpCgojIE1hdGNoIHRoZSAzMDAgb3V0cHV0cyB3ZSdyZSB1c2luZyBpbiB0aGUgdHJhaW5pbmcgZGF0YQpYX3dhdmUwMV90cmFpbiA8LSBYX3dhdmUwMVsxOjMwMCxdCgpgYGAKCmBgYHtyfQpZX2NvbnN0X3dhdmUwMSA8LSBlbnNfd2F2ZTAxX212JGRhdG1hdFssIHluYW1lc19jb25zdF0KCllfY29uc3Rfd2F2ZTAxX3NjYWxlZCA8LSBzd2VlcChZX2NvbnN0X3dhdmUwMSwgMiwgU1RBVFMgPSBzY2FsZXZlYywgRlVOID0gJy8nICkKCgpgYGAKCiMjIEhvdyBtYW55IHJ1biBmYWlsdXJlcyB3ZXJlIHRoZXJlPyAgCgpUaGVyZSBhcmUgbm8gTkFzIGJ1dCBzb21lIHJlbGF0aXZlIGh1bWlkaXR5IHZhbHVlcyBhcmUgaW5maW5pdGUuClRoZXJlIGFyZSBubyAibG93IE5QUCIgZW5zZW1ibGUgbWVtYmVycwoKYGBge3J9Cgpsb3dfbnBwX2l4X3dhdmUwMSA8LSB3aGljaChlbnNfd2F2ZTAxX212JGRhdG1hdFssJ25wcF9ubGltX2xuZF9zdW0nXSA8IDFlNSkKCm1pbihlbnNfd2F2ZTAxX212JGRhdG1hdFssJ25wcF9ubGltX2xuZF9zdW0nXSkKCllfd2F2ZTAxX25sZXZlbDBfaXggPC0gd2hpY2goaXMubmEoZW5zX3dhdmUwMV9tdiRkYXRtYXRbLCduYnBfbG5kX3N1bSddKSkKCmFsbChpcy5maW5pdGUoZW5zX3dhdmUwMV9tdiRkYXRtYXQpKQoKd2hpY2goIWlzLmZpbml0ZShlbnNfd2F2ZTAxX212JGRhdG1hdCksIGFyci5pbmQgPSBUUlVFKQoKZW5zX3dhdmUwMV9tdiRkYXRtYXRbd2hpY2goIWlzLmZpbml0ZShlbnNfd2F2ZTAxX212JGRhdG1hdCksIGFyci5pbmQgPSBUUlVFKV0KCmNvbG5hbWVzKGVuc193YXZlMDFfbXYkZGF0bWF0KVs5XQoKCmBgYAoKIyBFbnNlbWJsZSBiZWhhdmlvdXIgaW4ga2V5IChjb25zdHJhaW5pbmcpIG91dHB1dHMuIAoKR2xvYmFsIG1lYW4gZm9yIHRoZSAyMCB5ZWFycyBhdCB0aGUgZW5kIG9mIHRoZSAyMHRoIENlbnR1cnkuIFRoZXJlIGlzIHN0aWxsIGEgc2lnbmlmaWNhbnQgbG93IGJpYXMgb24gY1ZlZyBvdXRwdXQuCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDh9Cgp3YXZlMDBjb2wgPC0gJ3NreWJsdWUyJwp3YXZlMDFjb2wgPC0gJ3RvbWF0bzInCgp3YXZlMDBjb2wgPC0gJ2RvZGdlcmJsdWUyJwp3YXZlMDFjb2wgPC0gJ2ZpcmVicmljaycKcmFuZ2Vjb2wgPC0gJ2dyZXknCgoKIyBIaXN0b2dyYW0gb2YgbGV2ZWwgMSBjb25zdHJhaW50cwpoY29sID0gJ2RhcmtncmV5JwpsY29sID0gJ2JsYWNrJwoKI3BkZihmaWxlID0gJ2ZpZ3MvbGV2ZWxfMl9jb25zdHJhaW50c19oaXN0cy5wZGYnLCB3aWR0aCA9IDYsIGhlaWdodCA9IDUpCnBhcihtZnJvdyA9IGMoMiwyKSwgZmcgPSAnZGFya2dyZXknLCBsYXMgPSAxLCBvbWEgPSBjKDAuMSwgMC4xLCA0LCAwLjEpKQoKdHJ1bmMgPC0gZnVuY3Rpb24oeCwgdmVjKXsKICAKICBkYXQgPC0geFt4IDwgbWF4KHZlYykgJiB4ID4gbWluKHZlYykgIF0KICAKICBkYXQKICAKfQoKCmggPC0gaGlzdChZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywnbmJwX2xuZF9zdW0nXSwgbWFpbiA9ICdOQlAnLCB4bGFiID0gJ0d0Qy95ZWFyJywgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwxNTApKQpoaXN0KHRydW5jKFlfY29uc3Rfd2F2ZTAxX3NjYWxlZCBbLCduYnBfbG5kX3N1bSddLCBoJGJyZWFrcykgLAogICAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsMTUwKSAsIGJyZWFrcyA9IGgkYnJlYWtzLCBhZGQgPSBUUlVFKQoKcnVnKFlfY29uc3Rfc3Rhbl9zY2FsZWRbJ25icF9sbmRfc3VtJ10sIGx3ZCA9IDIpCgpwb2x5Z29uKHggPSBjKDAsIDEwMCwgMTAwLCAwKSwgeSA9IGMoMCwgMCwgMTAwMCwgMTAwMCksCiAgICAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHJhbmdlY29sLCA2MCksCiAgICAgICAgYm9yZGVyID0gbWFrZVRyYW5zcGFyZW50KHJhbmdlY29sKSkKCmggPC0gaGlzdChZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywnbnBwX25saW1fbG5kX3N1bSddLGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsMTUwKSwgbWFpbiA9ICdOUFAnLCB4bGFiID0gJ0d0Qy95ZWFyJykKaGlzdCh0cnVuYyhZX2NvbnN0X3dhdmUwMV9zY2FsZWQgWywnbnBwX25saW1fbG5kX3N1bSddLCBoJGJyZWFrcykgLCAKICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sKSAsIGJyZWFrcyA9IGgkYnJlYWtzLCBhZGQgPSBUUlVFKQoKcnVnKFlfY29uc3Rfc3Rhbl9zY2FsZWRbJ25wcF9ubGltX2xuZF9zdW0nXSwgbHdkID0gMikKCnBvbHlnb24oeCA9IGMoMzUsIDgwLCA4MCwgMzUpLCB5ID0gYygwLCAwLCAxMDAwLCAxMDAwKSwKICAgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wsIDYwKSwKICAgICAgICBib3JkZXIgPSBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wpKQoKCmggPC0gaGlzdChZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywnY1NvaWxfbG5kX3N1bSddLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLDE1MCksIG1haW4gPSAnU29pbCBDYXJib24nLCB4bGFiID0gJ0d0QycpCmhpc3QodHJ1bmMoWV9jb25zdF93YXZlMDFfc2NhbGVkIFssJ2NTb2lsX2xuZF9zdW0nXSwgaCRicmVha3MpICwgCiAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwxNTApICwgYnJlYWtzID0gaCRicmVha3MsIGFkZCA9IFRSVUUpCgpydWcoWV9jb25zdF9zdGFuX3NjYWxlZFsnY1NvaWxfbG5kX3N1bSddLCBsd2QgPSAyKQoKcG9seWdvbih4ID0gYyg3NTAsIDMwMDAsIDMwMDAsIDc1MCksIHkgPSBjKDAsIDAsIDEwMDAsIDEwMDApLAogICAgICAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCwgNjApLAogICAgICAgIGJvcmRlciA9IG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCkpCgpoIDwtIGhpc3QoWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLDE1MCksIG1haW4gPSAnVmVnZXRhdGlvbiBDYXJib24nLCB4bGFiID0gJ0d0QycpCmhpc3QodHJ1bmMoWV9jb25zdF93YXZlMDFfc2NhbGVkIFssJ2NWZWdfbG5kX3N1bSddLCBoJGJyZWFrcykgLCAKICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwxNTApICAsIGJyZWFrcyA9IGgkYnJlYWtzLCBhZGQgPSBUUlVFKQoKcnVnKFlfY29uc3Rfc3Rhbl9zY2FsZWRbJ2NWZWdfbG5kX3N1bSddLCBsd2QgPSAyKQoKcG9seWdvbih4ID0gYygzMDAsIDgwMCwgODAwLCAzMDApLCB5ID0gYygwLCAwLCAxMDAwLCAxMDAwKSwKICAgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wsIDYwKSwKICAgICAgIGJvcmRlciA9ICBtYWtlVHJhbnNwYXJlbnQocmFuZ2Vjb2wpKQoKI2Rldi5vZmYoKQoKcmVzZXQoKQoKbGVnZW5kKCd0b3AnLCBob3JpeiA9IFRSVUUsIGZpbGwgPSBjKG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDE1MCksIG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDE1MCksIG1ha2VUcmFuc3BhcmVudChyYW5nZWNvbCwgNjApKSwgbGVnZW5kID0gYygnV2F2ZTAwJywgJ1dhdmUwMScsICdBVyByYW5nZScpKQpgYGAKIyMgV2hhdCBwcm9wb3J0aW9uIG9mIG1vZGVscyAqbm93KiBmYWxsIHdpdGhpbiBBbmR5J3MgY29uc3RyYWludHM/CgpBIHRoaXJkISBCZXR0ZXIgdGhhbiBiZWZvcmUsIGJ1dCBzdGlsbCBub3QgZ3JlYXQuIFBvaW50aW5nIGF0IGEgc2lnbmlmaWNhbnQgbW9kZWwgZGlzY3JlcGVuY3kgaW4gY1ZlZwoKYGBge3J9CkFXX2NvbnN0cmFpbnRzIDwtIG1hdHJpeChucm93ID0gMiwgbmNvbCA9IGxlbmd0aCh5bmFtZXNfY29uc3QpKQoKQVdfY29uc3RyYWludHNbMSxdIDwtIGMoMCwgMzUsIDc1MCwgMzAwKQpBV19jb25zdHJhaW50c1syLF0gPC0gYygxMDAsIDgwLCAzMDAwLCA4MDApCgpjb2xuYW1lcyhBV19jb25zdHJhaW50cykgPC0geW5hbWVzX2NvbnN0CnJvd25hbWVzKEFXX2NvbnN0cmFpbnRzKSA8LSBjKCdtaW4nLCAnbWF4JykKCgojIGNvbmZvcm0gdG8gQW5keSdzIGJhc2ljIGNvbnN0cmFpbnRzCiNsZXZlbDJfaXhfd2F2ZTAxIDwtIHdoaWNoKGFwcGx5KFlfY29uc3Rfd2F2ZTAxX3NjYWxlZCwgMSwgRlVOID0gd2l0aGluUmFuZ2UsIG1heGVzICA9IEFXX2NvbnN0cmFpbnRzWzIsXSwgbWlucyA9IEFXX2NvbnN0cmFpbnRzWzEsXSApKQoKI25sZXZlbDJfaXhfd2F2ZTAxIDwtIHdoaWNoKGFwcGx5KFlfY29uc3Rfd2F2ZTAxX3NjYWxlZCwgMSwgRlVOID0gd2l0aGluUmFuZ2UsIG1heGVzICA9IEFXX2NvbnN0cmFpbnRzWzIsXSwgbWlucyA9IEFXX2NvbnN0cmFpbnRzWzEsXSApID09IEZBTFNFKQoKbGV2ZWwyX2l4X3dhdmUwMSA8LSB3aGljaChZX2NvbnN0X3dhdmUwMV9zY2FsZWRbLCduYnBfbG5kX3N1bSddID4gMCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF93YXZlMDFfc2NhbGVkWywnbnBwX25saW1fbG5kX3N1bSddID4gMzUgJiBZX2NvbnN0X3dhdmUwMV9zY2FsZWRbLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF93YXZlMDFfc2NhbGVkWywnY1NvaWxfbG5kX3N1bSddID4gNzUwICYgWV9jb25zdF93YXZlMDFfc2NhbGVkWywnY1NvaWxfbG5kX3N1bSddIDwgMzAwMCAmCiAgICAgICAgICAgICAgICAgIFlfY29uc3Rfd2F2ZTAxX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddID4gMzAwICYgWV9jb25zdF93YXZlMDFfc2NhbGVkWywnY1ZlZ19sbmRfc3VtJ10gPCA4MDAKICAgICAgICAgICAgICAgICAgKQoKCmBgYAoKT2YgdGhlIDMwMCBtZW1iZXJzIG9mIHRoZSB3YXZlMDEgZW5zZW1ibGUsIDEwMCBwYXNzIEFuZHkgV2lsdHNoaXJlJ3MgTGV2ZWwgMiBjb25zdHJhaW50cy4KCmBgYHtyfQoKbGVuZ3RoKGxldmVsMl9peF93YXZlMDEpCgpgYGAKClBhaXJzIHBsb3Qgb2YgdGhlIGlucHV0cyB0aGF0IHBhc3MgdGhlIGNvbnN0cmFpbnRzIHdpdGggcmVzcGVjdCB0byB0aGUgbGltaXRzIG9mIHRoZSBvcmlnaW5hbCBlbnNlbWJsZS4KCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEwfQoKcGFpcnMoWF93YXZlMDFbbGV2ZWwyX2l4X3dhdmUwMSwgXSwKICAgICAgeGxpbSA9IGMoMCwxKSwKICAgICAgeWxpbSA9IGMoMCwxKSwKICAgICAgZ2FwID0gMCwKICAgICAgbG93ZXIucGFuZWwgPSBOVUxMLAogICAgICBwY2ggPSAyMCwKICAgICAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwyMDApCiAgICAgICkKCmBgYAoKIyMgVGltZXNlcmllcyBvZiBtZWFuIGNhcmJvbiBjeWNsZSBwcm9wZXJ0aWVzIG92ZXIgd2hvbGUgcnVuLgoKYGBge3J9CgojIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIExvYWQgZW5zZW1ibGUKIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKaWYgKGZpbGUuZXhpc3RzKCJlbnNlbWJsZV90aW1lc2VyaWVzX3dhdmUwMS5yZGF0YSIpKSB7CiAgbG9hZCgiZW5zZW1ibGVfdGltZXNlcmllc193YXZlMDEucmRhdGEiKQp9IGVsc2UgewogIAogICMgcHJpbWFyeSBjYXJib24gY3ljbGUgb3V0cHV0cwogIG5wcF9lbnMgPC0gbWFrZVRpbWVzZXJpZXNFbnNlbWJsZSh2YXJpYWJsZSA9ICJucHBfbmxpbV9sbmRfc3VtIikgLyAoMWUxMi95c2VjKQogIG5icF9lbnMgPC0gIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUodmFyaWFibGUgPSAibmJwX2xuZF9zdW0iKSAvICgxZTEyL3lzZWMpCiAgY1NvaWxfZW5zIDwtICBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKHZhcmlhYmxlID0gImNTb2lsX2xuZF9zdW0iKSAvIDFlMTIKICBjVmVnX2VucyA8LSAgbWFrZVRpbWVzZXJpZXNFbnNlbWJsZSh2YXJpYWJsZSA9ICJjVmVnX2xuZF9zdW0iKSAvIDFlMTIKICAKICAKICBsYWlfbG5kX21lYW5fZW5zIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUodmFyaWFibGUgPSAibGFpX2xuZF9tZWFuIikKICAKICAjIGZsdXhlcwogIHJoX2xuZF9zdW1fZW5zIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUodmFyaWFibGUgPSAicmhfbG5kX3N1bSIpIC8gKDFlMTIveXNlYykKICBmTHVjX2xuZF9zdW1fZW5zIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUodmFyaWFibGUgPSAiZkx1Y19sbmRfc3VtIikgLyAoMWUxMi95c2VjKQogIGZIYXJ2ZXN0X2xuZF9zdW1fZW5zIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUodmFyaWFibGUgPSAiZkhhcnZlc3RfbG5kX3N1bSIpIC8gKDFlMTIveXNlYykKICAKICAKICAjIGZyYWN0aW9ucwogIHRyZWVGcmFjX2xuZF9tZWFuX2VucyA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKHZhcmlhYmxlID0gInRyZWVGcmFjX2xuZF9tZWFuIikKICBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUodmFyaWFibGUgPSAic2hydWJGcmFjX2xuZF9tZWFuIikKICBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUodmFyaWFibGUgPSAiYmFyZXNvaWxGcmFjX2xuZF9tZWFuIikKICAjYzNQZnRGcmFjX2xuZF9tZWFuX2VucyA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKHZhcmlhYmxlID0gImMzUGZ0RnJhY19sbmRfbWVhbl9lbnMiKQogICNjNFBmdEZyYWNfbG5kX21lYW5fZW5zIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUodmFyaWFibGUgPSAiYzRQZnRGcmFjX2xuZF9tZWFuX2VucyIpCiAgCiAgc2F2ZShucHBfZW5zLCBuYnBfZW5zLCBjU29pbF9lbnMsIGNWZWdfZW5zLCBsYWlfbG5kX21lYW5fZW5zLCByaF9sbmRfc3VtX2VucywgZkx1Y19sbmRfc3VtX2VucywgZkhhcnZlc3RfbG5kX3N1bV9lbnMsCiAgICAgICB0cmVlRnJhY19sbmRfbWVhbl9lbnMsIHNocnViRnJhY19sbmRfbWVhbl9lbnMsIGJhcmVzb2lsRnJhY19sbmRfbWVhbl9lbnMsIGZpbGUgPSAiZW5zZW1ibGVfdGltZXNlcmllc193YXZlMDEucmRhdGEiICkKICAKfQoKdG90YWxfbGFuZF9jYXJib25fZW5zIDwtIGNTb2lsX2VucyArIGNWZWdfZW5zCgpgYGAKCmBgYHtyfQoKCm1ha2VUaW1lc2VyaWVzRW5zZW1ibGUgPC0gZnVuY3Rpb24oZW5zbG9jLCB2YXJpYWJsZSwgbnN0YXJ0LCBuZW5kLCBudHMgPSAxNjQsIGNuID0gMTg1MDoyMDEzKXsKICAKICBuZW5zIDwtIChuZW5kIC0gbnN0YXJ0KSArIDEKICAjIG5lbnMgaXMgbnVtYmVyIG9mIGVuc2VtYmxlIG1lbWJlcnMKICAjIG50cyBsZW5ndGggb2YgdGltZXNlcmllcwogICMgY24gaXMgY29sbmFtZXMoKQogIGRhdG1hdCA8LSBtYXRyaXgoTkEsIG5yb3cgPSBuZW5zLCBuY29sID0gbnRzKQogIGNvbG5hbWVzKGRhdG1hdCkgPC0gY24KICAKICBlbnNsaXN0IDwtIHBhc3RlKCJQIiwgZm9ybWF0Qyhuc3RhcnQ6bmVuZCwgd2lkdGg9NCwgZmxhZz0iMCIpLCBzZXA9IiIpCiAgI2Zsb2MgPC0gcGFzdGUwKGVuc2xvYyxlbnNtZW1iZXIsc3ViZGlyKQogIAogIGZvcihpIGluIDE6bmVucyl7CiAgICAKICAgIHZlYyA8LSByZXAoTkEsbnRzKQogICAgCiAgICBlbnNtZW1iZXIgPC0gZW5zbGlzdFtpXSAKICAgIAogICAgZm4gPC0gcGFzdGUwKGVuc2xvYyxlbnNtZW1iZXIsJy9zdGF0cy8nLCdKVUxFUy1FUy0xcDBfJyxlbnNtZW1iZXIsJ19Bbm51YWxfZ2xvYmFsLm5jJykKICAgIAogICAgCiAgICB0cnkobmMgPC0gbmNfb3BlbihwYXN0ZTAoZm4pKSkKICAgIHRyeShkYXQgPC0gZXh0cmFjdFRpbWVzZXJpZXMobmMsIHZhcmlhYmxlKSkKICAgIAogICAgZGF0bWF0W2ksIF0gPC0gZGF0CiAgICBuY19jbG9zZShuYykKICB9CiAgZGF0bWF0Cn0KCmBgYAoKCmBgYHtyfQoKbnN0YXJ0IDwtIDQ5OQpuZW5kIDwtIDc5OAoKaWYgKGZpbGUuZXhpc3RzKCJlbnNlbWJsZV90aW1lc2VyaWVzX3dhdmUwMS5yZGF0YSIpKSB7CiAgbG9hZCgiZW5zZW1ibGVfdGltZXNlcmllc193YXZlMDEucmRhdGEiKQp9IGVsc2UgewogIAogICMgcHJpbWFyeSBjYXJib24gY3ljbGUgb3V0cHV0cwogICBucHBfZW5zX3dhdmUwMSA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvYyxuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLCB2YXJpYWJsZSA9ICJucHBfbmxpbV9sbmRfc3VtIikgLyAoMWUxMi95c2VjKQogICBuYnBfZW5zX3dhdmUwMSA8LSAgbWFrZVRpbWVzZXJpZXNFbnNlbWJsZShlbnNsb2MgPSBlbnNsb2MsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCx2YXJpYWJsZSA9ICJuYnBfbG5kX3N1bSIpIC8gKDFlMTIveXNlYykKICAgY1NvaWxfZW5zX3dhdmUwMSA8LSAgbWFrZVRpbWVzZXJpZXNFbnNlbWJsZShlbnNsb2MgPSBlbnNsb2MsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCx2YXJpYWJsZSA9ICJjU29pbF9sbmRfc3VtIikgLyAxZTEyCiAgIGNWZWdfZW5zX3dhdmUwMSA8LSAgbWFrZVRpbWVzZXJpZXNFbnNlbWJsZShlbnNsb2MgPSBlbnNsb2MsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCx2YXJpYWJsZSA9ICJjVmVnX2xuZF9zdW0iKSAvIDFlMTIKICAjIAogICMgCiAgIGxhaV9sbmRfbWVhbl9lbnNfd2F2ZTAxIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jLG5zdGFydCA9IG5zdGFydCwgbmVuZCA9IG5lbmQsdmFyaWFibGUgPSAibGFpX2xuZF9tZWFuIikKICAjIAogICMgIyBmbHV4ZXMKICAgcmhfbG5kX3N1bV9lbnNfd2F2ZTAxIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jLG5zdGFydCA9IG5zdGFydCwgbmVuZCA9IG5lbmQsIHZhcmlhYmxlID0gInJoX2xuZF9zdW0iKSAvICgxZTEyL3lzZWMpCiAgIGZMdWNfbG5kX3N1bV9lbnNfd2F2ZTAxIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jLG5zdGFydCA9IG5zdGFydCwgbmVuZCA9IG5lbmQsIHZhcmlhYmxlID0gImZMdWNfbG5kX3N1bSIpIC8gKDFlMTIveXNlYykKICAgZkhhcnZlc3RfbG5kX3N1bV9lbnNfd2F2ZTAxIDwtIG1ha2VUaW1lc2VyaWVzRW5zZW1ibGUoZW5zbG9jID0gZW5zbG9jLG5zdGFydCA9IG5zdGFydCwgbmVuZCA9IG5lbmQsIHZhcmlhYmxlID0gImZIYXJ2ZXN0X2xuZF9zdW0iKSAvICgxZTEyL3lzZWMpCiAgIyAKICAjIAogICMgIyBmcmFjdGlvbnMKICAgdHJlZUZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMSA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvYyxuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLCAgdmFyaWFibGUgPSAidHJlZUZyYWNfbG5kX21lYW4iKQogICBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMSA8LSBtYWtlVGltZXNlcmllc0Vuc2VtYmxlKGVuc2xvYyA9IGVuc2xvYyxuc3RhcnQgPSBuc3RhcnQsIG5lbmQgPSBuZW5kLCAgdmFyaWFibGUgPSAic2hydWJGcmFjX2xuZF9tZWFuIikKICAgYmFyZXNvaWxGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEgPC0gbWFrZVRpbWVzZXJpZXNFbnNlbWJsZShlbnNsb2MgPSBlbnNsb2MsbnN0YXJ0ID0gbnN0YXJ0LCBuZW5kID0gbmVuZCwgIHZhcmlhYmxlID0gImJhcmVzb2lsRnJhY19sbmRfbWVhbiIpCiAgCiAgIHNhdmUobnBwX2Vuc193YXZlMDEsCiAgICAgICAgbmJwX2Vuc193YXZlMDEsCiAgICAgICAgY1NvaWxfZW5zX3dhdmUwMSwKICAgICAgICBjVmVnX2Vuc193YXZlMDEsCiAgICAgICAgbGFpX2xuZF9tZWFuX2Vuc193YXZlMDEsCiAgICAgICAgcmhfbG5kX3N1bV9lbnNfd2F2ZTAxLAogICAgICAgIGZMdWNfbG5kX3N1bV9lbnNfd2F2ZTAxLAogICAgICAgIGZIYXJ2ZXN0X2xuZF9zdW1fZW5zX3dhdmUwMSwKICAgICAgICB0cmVlRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxLAogICAgICAgIHNocnViRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxLAogICAgICAgIGJhcmVzb2lsRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxLAogICAgICAgZmlsZSA9ICJlbnNlbWJsZV90aW1lc2VyaWVzX3dhdmUwMS5yZGF0YSIgKQogICAKfQoKI3RvdGFsX2xhbmRfY2FyYm9uX2Vuc193YXZlMDEgPC0gY1NvaWxfZW5zX3dhdmUwMSArIGNWZWdfZW5zX3dhdmUwMQoKYGBgCgoKCmBgYHtyIHBsb3QtY2FyYm9uLWN5Y2xlLXRpbWVzZXJpZXMtcHJpbWFyeSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMn0KCmxjb2xfd2F2ZTAgPC0gbWFrZVRyYW5zcGFyZW50KCdkb2RnZXJibHVlMicsICAxMjApCmxjb2xfd2F2ZTAxIDwtIG1ha2VUcmFuc3BhcmVudCgnZmlyZWJyaWNrJywgIDEyMCkKbGNvbF93YXZlMDFfbGV2ZWwyIDwtICdnb2xkJwpzdGFuY29sID0gJ2JsYWNrJwoKbGluZVBsb3RNdWx0aUVucyA8LSBmdW5jdGlvbih5ZWFycywgZW5zMSwgZW5zMiwgZW5zMywgY29sMSwgY29sMiwgY29sMywgeWxhYiwgbWFpbiwgeWxpbSA9IE5VTEwpewogICMgUGxvdCB3YXZlMDAgYW5kIHdhdmUwMSB0aW1lc2VyaWVzIG9uIHRvcCBvZiBvbmUgYW5vdGhlcgogIAogIG50IDwtIGxlbmd0aCh5ZWFycykgCiAgaWYoaXMubnVsbCh5bGltKSl7CiAgICAKICB5bGltID0gcmFuZ2UoYyhlbnMxWywxXSwgZW5zMVssbnRdLCBlbnMyWywxXSwgZW5zMlsgLG50XSwgZW5zM1ssMV0sIGVuczNbLCBudF0pKQogIH0KICAKICBlbHNlIHlsaW0gPC0geWxpbQogIAogIG1hdHBsb3QoeWVhcnMsIHQoZW5zMSksIHR5cGUgPSAnbCcsIGx0eSA9ICdzb2xpZCcseWxpbSA9IHlsaW0sIGNvbCA9IGNvbDEsCiAgICAgICAgeWxhYiA9IHlsYWIsIG1haW4gPSBtYWluLCB4bGFiID0gJycsCiAgICAgICAgYnR5ID0gJ24nKQogIG1hdGxpbmVzKHllYXJzLCB0KGVuczIpLCBjb2wgPSBjb2wyLCBsdHkgPSAnc29saWQnKQogICAgbWF0bGluZXMoeWVhcnMsIHQoZW5zMyksIGNvbCA9IGNvbDMsIGx0eSA9ICdzb2xpZCcpCn0KCgpwYXIobWZyb3c9IGMoMyw0KSwgbGFzID0gMSkKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IG5wcF9lbnMsIGVuczIgPSBucHBfZW5zX3dhdmUwMSwgZW5zMyA9IG5wcF9lbnNfd2F2ZTAxW2xldmVsMl9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnTlBQJykKCmxpbmVzKHllYXJzLG5wcF9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gIG5icF9lbnMsIGVuczIgPSBuYnBfZW5zX3dhdmUwMSwgZW5zMyA9IG5icF9lbnNfd2F2ZTAxW2xldmVsMl9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSxjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdOQlAnLCB5bGltID0gYygtMTAsMTApKQoKbGluZXMoeWVhcnMsIG5icF9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gY1NvaWxfZW5zLCBlbnMyID0gY1NvaWxfZW5zX3dhdmUwMSwgZW5zMyA9IGNTb2lsX2Vuc193YXZlMDFbbGV2ZWwyX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdjU29pbCcsIHlsaW0gPSByYW5nZShjKGNTb2lsX2Vuc1ssMV0sIGNTb2lsX2Vuc1ssMTY0XSkpKQoKbGluZXMoeWVhcnMsIGNTb2lsX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBjVmVnX2VucywgZW5zMiA9IGNWZWdfZW5zX3dhdmUwMSwgZW5zMyA9IGNWZWdfZW5zX3dhdmUwMVtsZXZlbDJfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2NWZWcnKQoKbGluZXMoeWVhcnMsIGNWZWdfc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGxhaV9sbmRfbWVhbl9lbnMsIGVuczIgPSBsYWlfbG5kX21lYW5fZW5zX3dhdmUwMSwgZW5zMyA9IGxhaV9sbmRfbWVhbl9lbnNfd2F2ZTAxW2xldmVsMl9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnTGFpJykKCmxpbmVzKHllYXJzLCBsYWlfbG5kX21lYW5fc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IHJoX2xuZF9zdW1fZW5zLCBlbnMyID0gcmhfbG5kX3N1bV9lbnNfd2F2ZTAxLCBlbnMzID0gcmhfbG5kX3N1bV9lbnNfd2F2ZTAxW2xldmVsMl9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ1JIJykKCmxpbmVzKHllYXJzLCByaF9sbmRfc3VtX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBmTHVjX2xuZF9zdW1fZW5zLCBlbnMyID0gZkx1Y19sbmRfc3VtX2Vuc193YXZlMDEsIGVuczMgPSBmTHVjX2xuZF9zdW1fZW5zX3dhdmUwMVtsZXZlbDJfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2ZMdWMnKQoKbGluZXMoeWVhcnMsIGZMdWNfbG5kX3N1bV9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKbGluZVBsb3RNdWx0aUVucyh5ZWFycyA9IHllYXJzLCBlbnMxID0gZkhhcnZlc3RfbG5kX3N1bV9lbnMsIGVuczIgPSBmSGFydmVzdF9sbmRfc3VtX2Vuc193YXZlMDEsCiAgICAgICAgICAgICAgICAgZW5zMyA9IGZIYXJ2ZXN0X2xuZF9zdW1fZW5zX3dhdmUwMVtsZXZlbDJfaXhfd2F2ZTAxLCBdLAogICAgICAgICAgICAgICAgIGNvbDEgPSBsY29sX3dhdmUwLCBjb2wyID0gbGNvbF93YXZlMDEsIGNvbDMgPSBsY29sX3dhdmUwMV9sZXZlbDIsCiAgICAgICAgICAgICAgICAgeWxhYiA9ICdHdEMnLCBtYWluID0gJ2ZIYXJ2ZXN0JykKCmxpbmVzKHllYXJzLCBmSGFydmVzdF9sbmRfc3VtX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSB0cmVlRnJhY19sbmRfbWVhbl9lbnMsIGVuczIgPSB0cmVlRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxLAogICAgICAgICAgICAgICAgIGVuczMgPSB0cmVlRnJhY19sbmRfbWVhbl9lbnNfd2F2ZTAxW2xldmVsMl9peF93YXZlMDEsIF0sCiAgICAgICAgICAgICAgICAgY29sMSA9IGxjb2xfd2F2ZTAsIGNvbDIgPSBsY29sX3dhdmUwMSwgY29sMyA9IGxjb2xfd2F2ZTAxX2xldmVsMiwKICAgICAgICAgICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAndHJlZWZyYWMnCiAgICAgICAgICAgICAgICAgKQoKbGluZXMoeWVhcnMsIHRyZWVGcmFjX2xuZF9tZWFuX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDIpCgpsaW5lUGxvdE11bHRpRW5zKHllYXJzID0geWVhcnMsIGVuczEgPSBzaHJ1YkZyYWNfbG5kX21lYW5fZW5zLCBlbnMyID0gc2hydWJGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDEsCiAgICAgICAgICAgICAgICAgZW5zMyA9IHNocnViRnJhY19sbmRfbWVhbl9lbnNbbGV2ZWwyX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdzaHJ1YmZyYWMnCikKCmxpbmVzKHllYXJzLCBzaHJ1YkZyYWNfbG5kX21lYW5fc3RhbiwgY29sID0gc3RhbmNvbCwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKCmxpbmVQbG90TXVsdGlFbnMoeWVhcnMgPSB5ZWFycywgZW5zMSA9IGJhcmVzb2lsRnJhY19sbmRfbWVhbl9lbnMsIGVuczIgPSBiYXJlc29pbEZyYWNfbG5kX21lYW5fZW5zX3dhdmUwMSwKICAgICAgICAgICAgICAgICBlbnMzID0gYmFyZXNvaWxGcmFjX2xuZF9tZWFuX2Vuc193YXZlMDFbbGV2ZWwyX2l4X3dhdmUwMSwgXSwKICAgICAgICAgICAgICAgICBjb2wxID0gbGNvbF93YXZlMCwgY29sMiA9IGxjb2xfd2F2ZTAxLCBjb2wzID0gbGNvbF93YXZlMDFfbGV2ZWwyLAogICAgICAgICAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdiYXJlc29pbGZyYWMnKQoKbGluZXMoeWVhcnMsIGJhcmVzb2lsRnJhY19sbmRfbWVhbl9zdGFuLCBjb2wgPSBzdGFuY29sLCBsdHkgPSAnc29saWQnLCBsd2QgPSAyKQoKcmVzZXQoKQoKbGVnZW5kKCdib3R0b21yaWdodCcsIGxlZ2VuZCA9IGMoJ3dhdmUwMCcsJ3dhdmUwMScsJ3dhdmUwMSBsZXZlbDInLCdzdGFuZGFyZCcpLCBsdHkgPSAnc29saWQnLCBsd2QgPSAxLjUsIGNvbCA9IGMobGNvbF93YXZlMCwgbGNvbF93YXZlMDEsIGxjb2xfd2F2ZTAxX2xldmVsMiwgc3RhbmNvbCksIGluc2V0ID0gYygwLjA1LCAwLjE1KSApCgoKYGBgClRoaXMgaXMgYSBwbG90IG9mIHRpbWVzZXJpZXMgb2YgV2F2ZTAwLCBXYXZlMDEsIGFuZCBsZXZlbDItY29uc3RyYWluZWQgd2F2ZTAxIG9uIHRvcCBvZiBvbmUgYW5vdGhlci4gV2Ugc2VlIHRoYXQgdGhlIHdhdmUwMSBpcyBjbG9zZXIgdG8gdGhlIHN0YW5kYXJkIHRoYW4gd2F2ZTAwLCBhbmQgdGhlIGxldmVsLTIgY29uc3RyYWluZWQgd2F2ZTAxIGVuc2VtYmxlIGlzIG9mdGVuIGNsb3NlciBhZ2Fpbi4gSG93ZXZlciwgdGhlcmUgYXJlIHN0aWxsIHF1aXRlIGxhcmdlIGRpc2NyZXBhbmNpZXMuIEZvciBleGFtcGxlLCBiYXJlc29pbGZyYWMgaXMgb2Z0ZW4gd2F5IHRvbyBoaWdoLCBzaHJ1YmZyYWMgaXMgb2Z0ZW4gdG9vIGxvdyAodGhvdWdoIGJvdGggdGhlc2Ugc3BhbiB0aGUgc3RhbmRhcmQpLiBUcmVlZnJhYyBpcyBhd2F5IGZyb20gemVybywgYnV0IHN0aWxsIG9mdGVuIHRvbyBsb3cgb3IgdG9vIGhpZ2guIFdoaWxlIGZIYXJ2ZXN0IGxvb2tzIGdvb2QsIGZMdWMgZG9lcyBub3QgYXBwZWFyIGNvbnN0cmFpbmVkIGJ5IHRoZSBwcm9jZXNzIGF0IGFsbC4gUkggKHNvaWwgcmVzcGlyYXRpb24pIGxvb2tzIHdlbGwgY29uc3RyYWluZWQsIHdoZXJlYXMgbGFpIGlzIG9mdGVuIHRvbyBsb3cuICAKCk9uZSB0aGluZyB3ZSBjb3VsZCBkbyBuZXh0IGlzIGNvbnN0cmFpbiBpbnB1dCBzcGFjZSBhZ2FpbiwgdXNpbmcgb2JzZXJ2YXRpb25zIG9yICJ0b2xlcmFuY2UgdG8gZXJyb3IiIG9uIHNvbWUgb3IgYWxsIG9mIHRoZXNlIG91dHB1dHMuICAKCldlIGNvdWxkIGFsc28gZXh0ZW5kIHNlbnNpdGl2aXR5IGFuYWx5c2lzIHRvIHdvcmsgb3V0IHdoYXQgY29udHJvbHMgZS5nLiB0cmVlZnJhYy4KCgojIEVtdWxhdG9yIGZpdHMKCldlIGhvcGUgdGhhdCBydW5uaW5nIHRoZSBuZXcgZW5zZW1ibGUgZ2l2ZXMgdXMgYSBiZXR0ZXIgZW11bGF0b3IsIGFuZCBhbGxvd3MgdXMgdG8gcnVsZSBvdXQgbW9yZSBpbnB1dCBzcGFjZS4KV2UgcGFydGljdWxhcmx5IGhvcGUgdGhhdCB0aGUgZW11bGF0b3IgaXMgYmV0dGVyIGZvciB0aG9zZSBtZW1iZXJzIHRoYXQgYXJlIGluc2lkZSBBVydzIGNvbnN0cmFpbnRzLgoKRmlyc3QsIHdlIGNhbiBsb29rIGF0IHRoZSBlbXVsYXRvciBlcnJvcnMgaW4gdHdvIGNhc2VzOiBUaGUgbGV2ZWwxYSBkYXRhIChhIGJhc2ljIGNhcmJvbiBjeWNsZSksIGFuZCB0aGVuIHdpdGggdGhlIFdhdmUwMSBkYXRhLCB3aGljaCBzaG91bGQgaGF2ZSBzaW1pbGFyIGNoYXJhY3RlcmlzdGljcy4gKFdlIHNob3VsZCBoYXZlIGVsaW1pbmF0ZWQgcmVhbGx5IGJhZCBzaW11bGF0aW9ucywgYnV0IHdhdmUwMSBpcyBub3QgY29uc3RyYWluZWQgdGhlIGRhdGEgcGVyZmVjdGx5IHRvIGJlIHdpdGhpbiBBVyBjb25zdHJhaW50cy4pCgoKYGBge3J9CiMjIEVtdWxhdG9yIGZpdCBsaXN0IG9mIGxldmVsIDFhIGVuc2VtYmxlCgojZml0X2xpc3RfY29uc3RfbGV2ZWwxYSA8LSBjcmVhdGVLbUZpdExpc3QoWCA9IFhfbGV2ZWwxYSwgWSA9IFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpCgpZX2NvbnN0X2xldmVsMWFfc2NhbGVkX2xpc3QgPC0gbWF0Mmxpc3QoWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkKCmZpdF9saXN0X2NvbnN0X2xldmVsMWEgPC0gbWNsYXBwbHkoWCA9IFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRfbGlzdCwgRlVOID0ga20sIGZvcm11bGEgPSB+LiwgZGVzaWduID0gWF9sZXZlbDFhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1jLmNvcmVzID0gNCwgY29udHJvbCA9IGxpc3QodHJhY2UgPSBGQUxTRSkpCgoKCgpgYGAKCgoKYGBge3J9CiMjIFJlbW92ZSBhbiBvdXRsaWVyIGZyb20gdGhlIG5ldyB3YXZlIGFuZCBidWlsZCBlbXVsYXRvcnMKIyBCaW5kIHRvZ2V0aGVyIHRoZSBpbnB1dCBtYXRyaWNlcyBhbmQgc2NhbGVkIG91dHB1dCBkYXRhClhfbGV2ZWwxYV93YXZlMDEgPC0gcmJpbmQoWF9sZXZlbDFhLCBYX3dhdmUwMV90cmFpbilbLTQ0MCwgXQoKWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWQgPC0gcmJpbmQoWV9jb25zdF9sZXZlbDFhX3NjYWxlZCwgWV9jb25zdF93YXZlMDFfc2NhbGVkKVstNDQwLCBdCgoKYXBwbHkoWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWQgLDIsIHdoaWNoLm1heCkKYXBwbHkoWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWQgLDIsIHdoaWNoLm1pbikKYGBgCgoKRm91bmQgdGhlIG91dGxpZXIgLSBsb29rcyBsaWtlIGl0J3MgNDQwCmBgYHtyfQoKZmluZE91dGxpZXJzIDwtIGZ1bmN0aW9uKHgsIHNkcyA9IDYpewogIAogIGl4IDwtIHdoaWNoKGFicyh4IC0gbWVhbih4KSkgPiAoc2RzICogc2QoeCkpKQogIAp9CgphcHBseShZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZCAsMiwgZmluZE91dGxpZXJzLCBzZHMgPSAxMCkKYGBgCgoKYGBge3J9CiMgQ3JlYXRlIGZpdCBsaXN0cyBmb3IgdGhlIGNvbWJpbmVkIGRhdGEKWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRfbGlzdCA8LSBtYXQybGlzdChZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZCkKCmZpdF9saXN0X2NvbnN0X2xldmVsMWFfd2F2ZTAxIDwtIG1jbGFwcGx5KFggPSBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZF9saXN0ICwgRlVOID0ga20sIGZvcm11bGEgPSB+LiwgZGVzaWduID0gWF9sZXZlbDFhX3dhdmUwMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYy5jb3JlcyA9IDQsIGNvbnRyb2wgPSBsaXN0KHRyYWNlID0gRkFMU0UpKQoKCmBgYAoKIyMgTGVhdmUtb25lLW91dCBhbmFseXNlcyBvZiBlbXVsYXRvciBwcmVkaWN0aW9uIGFjY3VyYWN5CgpgYGB7cn0KCmxvb2xpc3Rfa21fWV9sZXZlbDFhIDwtIG1jbGFwcGx5KFggPSBmaXRfbGlzdF9jb25zdF9sZXZlbDFhLCBGVU4gPSBsZWF2ZU9uZU91dC5rbSwgdHlwZSA9ICdVSycsIHRyZW5kLnJlZXN0aW0gPSBUUlVFKQoKbG9vbGlzdF9rbV9ZX2xldmVsMWFfd2F2ZTAxIDwtIG1jbGFwcGx5KFggPSBmaXRfbGlzdF9jb25zdF9sZXZlbDFhX3dhdmUwMSwgRlVOID0gbGVhdmVPbmVPdXQua20sIHR5cGUgPSAnVUsnLCB0cmVuZC5yZWVzdGltID0gVFJVRSkKCmBgYAoKCmBgYHtyfQoKbG9vc3RhdHNfa21fWV9sZXZlbDFhIDwtIGxhcHBseShmaXRfbGlzdF9jb25zdF9sZXZlbDFhLCBGVU4gPSBrbUxvb1N0YXRzKQpsb29zdGF0c19rbV9ZX2xldmVsMWFfd2F2ZTAxIDwtIGxhcHBseShmaXRfbGlzdF9jb25zdF9sZXZlbDFhX3dhdmUwMSwgRlVOID0ga21Mb29TdGF0cykKCmBgYAoKClRoZSB0b3Agcm93IHNob3dzIHRoZSBsZWF2ZS1vbmUtb3V0IHByZWRpY3Rpb24gYWNjdXJhY3kgb2YgdGhlIG9yaWdpbmFsIHdhdmUwMCBlbnNlbWJsZSwgYW5kIHRoZSBsb3dlciByb3cgdGhlIGVudGlyZSB3YXZlMDAgQU5EIHdhdmUwMSBlbnNlbWJsZSBjb21iaW5lZC4KCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDZ9CgojcGRmKGZpbGUgPSAnZmlncy9rbWxvb3N0YXRzX1lfbGV2ZWwxYS5wZGYnLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMikKcGFyKG1mcm93ID0gYygyLDQpLCBtYXIgPSBjKDMsNCwyLDIpLCBvbWEgPSBjKDQsNCw0LDAuMSkpCmZvcihpIGluIDE6bGVuZ3RoKGxvb2xpc3Rfa21fWV9sZXZlbDFhKSl7CiAgCiAgeSA8LSBZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywgaV0KICBsb28gPC0gbG9vbGlzdF9rbV9ZX2xldmVsMWFbW2ldXQogIHlsaW0gPC0gcmFuZ2UoYyhsb28kbWVhbiAtICgyKmxvbyRzZCksIGxvbyRtZWFuICsgKDIqbG9vJHNkKSkgKQogIHBsb3QoeSwgbG9vJG1lYW4sIHhsYWIgPSAnJywgeWxhYiA9ICcnLCBtYWluID0gJycgLCB5bGltID0geWxpbSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwgMTAwKSwKICAgICAgIHBjaCA9IDE5KQogIHNlZ21lbnRzKHgwID0geSwgeTAgPSBsb28kbWVhbiAtICgyKmxvbyRzZCkgICwgeDEgPSB5ICwgeTEgPSBsb28kbWVhbiArICgyKmxvbyRzZCksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDUwKSkKICBhYmxpbmUoMCwxKQogIGxlZ2VuZCgndG9wbGVmdCcsIGxlZ2VuZCA9IGNvbG5hbWVzKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpW2ldLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIgICkKICBsZWdlbmQoJ2JvdHRvbXJpZ2h0JyxsZWdlbmQgPSBwYXN0ZSgncG1hZSA9Jyxyb3VuZChsb29zdGF0c19rbV9ZX2xldmVsMWFbW2ldXSRwbWFlLDIpLCclJykgLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIpCgp9CgptdGV4dCgnQWN0dWFsJywgc2lkZSA9IDEsIGxpbmUgPSAxLCBvdXRlciA9IFRSVUUsIGNleCA9IDIgKQptdGV4dCgnUHJlZGljdGVkJywgc2lkZSA9IDIsIGxpbmUgPSAwLCBvdXRlciA9IFRSVUUsIGNleCA9IDIpIAptdGV4dCgnTGV2ZWwgMWEgZW5zZW1ibGUgb3V0cHV0cycsIHNpZGUgPSAzLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKQoKI2Rldi5vZmYoKQoKI3BkZihmaWxlID0gJ2ZpZ3Mva21sb29zdGF0c19ZX2xldmVsMWEucGRmJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCmZvcihpIGluIDE6bGVuZ3RoKGxvb2xpc3Rfa21fWV9sZXZlbDFhKSl7CiAgCiAgeSA8LSBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssIGldCiAgbG9vIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMVtbaV1dCiAgeWxpbSA8LSByYW5nZShjKGxvbyRtZWFuIC0gKDIqbG9vJHNkKSwgbG9vJG1lYW4gKyAoMipsb28kc2QpKSApCiAgcGxvdCh5LCBsb28kbWVhbiwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnJyAsIHlsaW0gPSB5bGltLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLCAxMDApLAogICAgICAgcGNoID0gMTkpCiAgc2VnbWVudHMoeDAgPSB5LCB5MCA9IGxvbyRtZWFuIC0gKDIqbG9vJHNkKSAgLCB4MSA9IHkgLCB5MSA9IGxvbyRtZWFuICsgKDIqbG9vJHNkKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwgMTAwKSkKICBhYmxpbmUoMCwxKQogIGxlZ2VuZCgndG9wbGVmdCcsIGxlZ2VuZCA9IGNvbG5hbWVzKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpW2ldLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIgICkKICBsZWdlbmQoJ2JvdHRvbXJpZ2h0JyxsZWdlbmQgPSBwYXN0ZSgncG1hZSA9Jyxyb3VuZChsb29zdGF0c19rbV9ZX2xldmVsMWFfd2F2ZTAxW1tpXV0kcG1hZSwyKSwnJScpICwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyKQoKfQoKbXRleHQoJ0FjdHVhbCcsIHNpZGUgPSAxLCBsaW5lID0gMSwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyICkKbXRleHQoJ1ByZWRpY3RlZCcsIHNpZGUgPSAyLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKSAKbXRleHQoJ0xldmVsIDFhIGVuc2VtYmxlIG91dHB1dHMnLCBzaWRlID0gMywgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikKCmBgYAoKYGBge3J9CiMgcmVtb3ZlIHRvIGxldmVsIDFhIFJlbGF0aXZlIHRvIHRvcGxldmVsX2l4IC0gdXNlZnVsIGZvciBwbG90dGluZyBldGMuCiN0b3BsZXZlbF90b19sZXZlbF8xYV9peCA8LSh0b3BsZXZlbF9peFstWV9ubGV2ZWwwX2l4XSlbbGV2ZWwxYV9peF0KCiMgU28gZnVydGhlciBjb25zdHJhaW5pbmcgdG8gbGV2ZWwgMiBjYW4gYmUgYXNzb2NpYXRlZCBiYWNrIHRvIHRoZSB0b3AgbGV2ZWwuCgpsZXZlbDJfaXggPC0gd2hpY2goWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ25icF9sbmRfc3VtJ10gPiAwICYKICAgICAgICAgICAgICAgICAgICBZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywnbnBwX25saW1fbG5kX3N1bSddID4gMzUgJiAgWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ25wcF9ubGltX2xuZF9zdW0nXSA8IDgwICYKICAgICAgICAgICAgICAgICAgICBZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywnY1NvaWxfbG5kX3N1bSddID4gNzUwICYgWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssJ2NTb2lsX2xuZF9zdW0nXSA8IDMwMDAgJgogICAgICAgICAgICAgICAgICBZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywnY1ZlZ19sbmRfc3VtJ10gPiAzMDAgJiBZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywnY1ZlZ19sbmRfc3VtJ10gPCA4MDAKICAgICAgICAgICAgICAgICAgCiAgKQoKbGV2ZWwyX2l4X2xldmVsMWFfd2F2ZTAxIDwtIHdoaWNoKFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkWywnbmJwX2xuZF9zdW0nXSA+IDAgJgogICAgICAgICAgICAgICAgICAgIFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkWywnbnBwX25saW1fbG5kX3N1bSddID4gMzUgJiBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssJ25wcF9ubGltX2xuZF9zdW0nXSA8IDgwICYKICAgICAgICAgICAgICAgICAgICBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkWywnY1NvaWxfbG5kX3N1bSddIDwgMzAwMCAmCiAgICAgICAgICAgICAgICAgIFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkWywnY1ZlZ19sbmRfc3VtJ10gPiAzMDAgJiBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFssJ2NWZWdfbG5kX3N1bSddIDwgODAwCiAgICAgICAgICAgICAgICAgICkKCmBgYAoKIyMgRW11bGF0b3IgYWNjdXJhY3kgb2YgbWVtYmVycyBmcm9tIHdhdmUgMDAgYW5kIHdhdmUgMDEgdGhhdCBwYXNzIGxldmVsIDIgKEFXJ3MpIGNvbnN0cmFpbnRzCgpXZSBzZWUgdGhhdCB0aGUgZXJyb3Igc3RhdHMgZm9yIHNvbWUgb2YgdGhlIG91dHB1dHMgZnJvbSB3YXZlMDEgYXJlIHdvcnNlLCBidXQgdGhlcmUgYXJlIG1hbnkgbW9yZSBlbnNlbWJsZSBtZW1iZXJzIHRoYXQgbGllIHdpdGhpbiB0aGUgY29uc3RyYWludHMgZm9yIHdhdmUgMDEuCgoicG1hZSIgaXMgInByb3BvcnRpb25hbCBtZWFuIGFic29sdWUgZXJyb3IiLCB3aGljaCBpcyB0aGUgbWVhbiBhYnNvbHV0ZSBlcnJvciBleHByZXNzZWQgYXMgYSBwZXJjZW50YWdlIG9mIHRoZSBvcmlnaW5hbCAobWluaW1hbGx5IGNvbnN0cmFpbmVkKSBlbnNlbWJsZSByYW5nZSBpbiB0aGF0IG91dHB1dC4gCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQoKI3BkZihmaWxlID0gJ2ZpZ3Mva21sb29zdGF0c19ZX2xldmVsMWEucGRmJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCnBhcihtZnJvdyA9IGMoMiw0KSwgbWFyID0gYygzLDQsMiwyKSwgb21hID0gYyg0LDQsNCwwLjEpKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lfbGV2ZWwxYSkpewogIAogIHkgPC0gWV9jb25zdF9sZXZlbDFhX3NjYWxlZFtsZXZlbDJfaXgsIGldCiAgbG9vIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhW1tpXV0KICB5bGltIDwtIHJhbmdlKGMobG9vJG1lYW5bbGV2ZWwyX2l4XSAtICgyKmxvbyRzZFtsZXZlbDJfaXhdKSwgbG9vJG1lYW5bbGV2ZWwyX2l4XSArICgyKmxvbyRzZFtsZXZlbDJfaXhdKSkgKQogIHBsb3QoeSwgbG9vJG1lYW5bbGV2ZWwyX2l4XSwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnJyAsIHlsaW0gPSB5bGltLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLCAxMDApLAogICAgICAgcGNoID0gMTkpCiAgc2VnbWVudHMoeDAgPSB5LCB5MCA9IGxvbyRtZWFuW2xldmVsMl9peF0gLSAoMipsb28kc2RbbGV2ZWwyX2l4XSkgICwgeDEgPSB5ICwgeTEgPSBsb28kbWVhbltsZXZlbDJfaXhdICsgKDIqbG9vJHNkW2xldmVsMl9peF0pLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLCAxMDApKQogIGFibGluZSgwLDEpCiAgbGVnZW5kKCd0b3BsZWZ0JywgbGVnZW5kID0gY29sbmFtZXMoWV9jb25zdF9sZXZlbDFhX3NjYWxlZClbaV0sIGJ0eSA9ICduJywgdGV4dC5mb250ID0gMiAgKQogIGxlZ2VuZCgnYm90dG9tcmlnaHQnLGxlZ2VuZCA9IHBhc3RlKCdwbWFlID0nLHJvdW5kKGxvb3N0YXRzX2ttX1lfbGV2ZWwxYVtbaV1dJHBtYWUsMiksJyUnKSAsIGJ0eSA9ICduJywgdGV4dC5mb250ID0gMikKCn0KCiNkZXYub2ZmKCkKCiNwZGYoZmlsZSA9ICdmaWdzL2ttbG9vc3RhdHNfWV9sZXZlbDFhLnBkZicsIHdpZHRoID0gMTIsIGhlaWdodCA9IDEyKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lfbGV2ZWwxYSkpewogIAogIHkgPC0gWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRbbGV2ZWwyX2l4X2xldmVsMWFfd2F2ZTAxLCBpXQogIGxvbyA8LSBsb29saXN0X2ttX1lfbGV2ZWwxYV93YXZlMDFbW2ldXQogIHlsaW0gPC0gcmFuZ2UoYyhsb28kbWVhbltsZXZlbDJfaXhfbGV2ZWwxYV93YXZlMDFdIC0gKDIqbG9vJHNkW2xldmVsMl9peF9sZXZlbDFhX3dhdmUwMV0pLCBsb28kbWVhbltsZXZlbDJfaXhfbGV2ZWwxYV93YXZlMDFdICsgKDIqbG9vJHNkW2xldmVsMl9peF9sZXZlbDFhX3dhdmUwMV0pKSApCiAgcGxvdCh5LCBsb28kbWVhbltsZXZlbDJfaXhfbGV2ZWwxYV93YXZlMDFdLCB4bGFiID0gJycsIHlsYWIgPSAnJywgbWFpbiA9ICcnICwgeWxpbSA9IHlsaW0sIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDEwMCksCiAgICAgICBwY2ggPSAxOSkKICBzZWdtZW50cyh4MCA9IHksIHkwID0gbG9vJG1lYW5bbGV2ZWwyX2l4X2xldmVsMWFfd2F2ZTAxXSAtICgyKmxvbyRzZFtsZXZlbDJfaXhfbGV2ZWwxYV93YXZlMDFdKSAgLCB4MSA9IHkgLCB5MSA9IGxvbyRtZWFuW2xldmVsMl9peF9sZXZlbDFhX3dhdmUwMV0gKyAoMipsb28kc2RbbGV2ZWwyX2l4X2xldmVsMWFfd2F2ZTAxXSksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDUwKSkKICBhYmxpbmUoMCwxKQogIGxlZ2VuZCgndG9wbGVmdCcsIGxlZ2VuZCA9IGNvbG5hbWVzKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpW2ldLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIgICkKICBsZWdlbmQoJ2JvdHRvbXJpZ2h0JyxsZWdlbmQgPSBwYXN0ZSgncG1hZSA9Jyxyb3VuZChsb29zdGF0c19rbV9ZX2xldmVsMWFfd2F2ZTAxW1tpXV0kcG1hZSwyKSwnJScpICwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyKQoKfQoKbXRleHQoJ0FjdHVhbCcsIHNpZGUgPSAxLCBsaW5lID0gMSwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyICkKbXRleHQoJ1ByZWRpY3RlZCcsIHNpZGUgPSAyLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKSAKbXRleHQoJ0xldmVsIDIgY29uc3RyYWluZWQgZW5zZW1ibGUgb3V0cHV0cycsIHNpZGUgPSAzLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKQoKYGBgCgojIyBEb2VzIHRoZSBlbXVsYXRvciBpbXByb3ZlIGlzIHlvdSBsb29rIGF0IG9ubHkgdGhlIDM3IG1lbWJlcnMgdGhhdCBwYXNzIGxldmVsIDIgY29uc3RyYWludHMgaW4gd2F2ZSAwMD8KVGhpcyBnaXZlcyB1cyBhbiBpZGVhIG9mIGhvdyBnb29kIHRoZSBlbXVsYXRvciBpcyB3aGVyZSBpdCByZWFsbHkgbWF0dGVycywgYW5kIGFzIHRoZSBtZW1iZXJzIGFyZSBjb25zaXN0ZW50LCBnaXZlcyB1cyBhIGZhaXJlciBpZGVhIG9mIHdoZXRoZXIgdGhlIGVtdWxhdG9ycyBoYXZlIGltcHJvdmVkIHdpdGggbW9yZSBtZW1iZXJzLgoKR29vZCBuZXdzIGlzLCB0aGUgZW11bGF0b3JzIGFyZSBtb3JlIGFjY3VyYXRlIGZvciB3YXZlMDEuCgpgYGB7cn0KCgprbUxvb1N0YXRzU3Vic2V0IDwtIGZ1bmN0aW9uIChrbSwgaXgsIHR5cGUgPSAiVUsiKSAKewogICMgQ2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgYSBzdWJzZXQgb2YgdGhlIG1lbWJlcnMgb2YgYSBrbSBmaXQgbGlzdAogICAgbG9vIDwtIGxlYXZlT25lT3V0LmttKGttLCB0eXBlID0gdHlwZSwgdHJlbmQucmVlc3RpbSA9IFRSVUUpCiAgICBwcmVkZGlmZiA8LSBsb28kbWVhbltpeF0gLSBrbUB5W2l4XQogICAgbWFlIDwtIG1lYW4oYWJzKHByZWRkaWZmKSkKICAgIHJtc2UgPC0gc3FydChtZWFuKHByZWRkaWZmXjIpKQogICAgbWF4ZXJyIDwtIG1heChwcmVkZGlmZikKICAgIGFic2RpZmYgPC0gYWJzKGRpZmYocmFuZ2Uoa21AeSkpKQogICAgcG1hZSA8LSAobWFlL2Fic2RpZmYpICogMTAwCiAgICByZXR1cm4obGlzdChsb28gPSBsb28sIG1hZSA9IG1hZSwgcG1hZSA9IHBtYWUsIG1heGVyciA9IG1heGVycikpCn0KCgpgYGAKCgpgYGB7cn0KCmxvb2xpc3Rfa21fWV9sZXZlbDFhX2xldmVsMiA8LSByYXBwbHkobG9vbGlzdF9rbV9ZX2xldmVsMWEsIGYgPSBmdW5jdGlvbih4KSB4W2xldmVsMl9peF0sIGhvdyA9ICJsaXN0IikKCmxvb2xpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMV9sZXZlbDIgPC0gcmFwcGx5KGxvb2xpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMSwgZiA9IGZ1bmN0aW9uKHgpIHhbbGV2ZWwyX2l4XSwgaG93ID0gImxpc3QiKQoKCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CgojcGRmKGZpbGUgPSAnZmlncy9rbWxvb3N0YXRzX1lfbGV2ZWwxYS5wZGYnLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMikKcGFyKG1mcm93ID0gYygyLDIpLCBtYXIgPSBjKDMsNCwyLDIpLCBvbWEgPSBjKDQsNCw0LDAuMSkpCmZvcihpIGluIDE6bGVuZ3RoKGxvb2xpc3Rfa21fWV9sZXZlbDFhX2xldmVsMikpewogIAogIHkgPC0gWV9jb25zdF9sZXZlbDFhX3NjYWxlZFtsZXZlbDJfaXgsIGldCiAgCiAgbG9vIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhX2xldmVsMltbaV1dCiAgeWxpbSA8LSByYW5nZShjKGxvbyRtZWFuLSAoMipsb28kc2QpLCBsb28kbWVhbiArICgyKmxvbyRzZCkpICkKICBwbG90KHksIGxvbyRtZWFuLCB4bGFiID0gJycsIHlsYWIgPSAnJywgbWFpbiA9ICcnICwgeWxpbSA9IHlsaW0sIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDI1MCksCiAgICAgICBwY2ggPSAxOSkKICBhcnJvd3MoeDAgPSB5LCB5MCA9IGxvbyRtZWFuIC0gKDIqbG9vJHNkKSAgLCB4MSA9IHkgLCB5MSA9IGxvbyRtZWFuICsgKDIqbG9vJHNkKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwgMTUwKSAsICBhbmdsZSA9IDkwLCBjb2RlID0gMywgbGVuZ3RoID0gMC4wMykKICAKICB5MSA8LSBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFtsZXZlbDJfaXgsIGldCiAgbG9vIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMV9sZXZlbDJbW2ldXQogIAogICAgcG9pbnRzKHkxLCBsb28kbWVhbiwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnJyAsIHlsaW0gPSB5bGltLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLCAyNTApLAogICAgICAgcGNoID0gMTkpCiAgYXJyb3dzKHgwID0geSwgeTAgPSBsb28kbWVhbiAtICgyKmxvbyRzZCkgICwgeDEgPSB5ICwgeTEgPSBsb28kbWVhbiArICgyKmxvbyRzZCksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsIDI1MCksICBhbmdsZSA9IDkwLCBjb2RlID0gMywgbGVuZ3RoID0gMC4wMykKICAKICAKICBhYmxpbmUoMCwxKQogIGxlZ2VuZCgndG9wbGVmdCcsIGxlZ2VuZCA9IGNvbG5hbWVzKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpW2ldLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIgICkKICBsZWdlbmQoJ2JvdHRvbXJpZ2h0JyxsZWdlbmQgPSBwYXN0ZSgncG1hZSA9Jyxyb3VuZChsb29zdGF0c19rbV9ZX2xldmVsMWFbW2ldXSRwbWFlLDIpLCclJykgLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIpCgp9CgptdGV4dCgnQWN0dWFsJywgc2lkZSA9IDEsIGxpbmUgPSAxLCBvdXRlciA9IFRSVUUsIGNleCA9IDIgKQptdGV4dCgnUHJlZGljdGVkJywgc2lkZSA9IDIsIGxpbmUgPSAwLCBvdXRlciA9IFRSVUUsIGNleCA9IDIpIAptdGV4dCgnTGV2ZWwgMiB3YXZlIDAwIGVuc2VtYmxlIG91dHB1dHMnLCBzaWRlID0gMywgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikKCnJlc2V0KCkKbGVnZW5kKCd0b3BsZWZ0JywgcGNoID0gMTksIGxlZ2VuZCA9IGMoJ3dhdmUwMCcsICd3YXZlMDEnKSwgY29sID0gYyh3YXZlMDBjb2wsIHdhdmUwMWNvbCApLCBob3JpeiA9IFRSVUUpCgoKYGBgCgpUaGVzZSBsZWF2ZS1vbmUtb3V0IHByZWRpY3Rpb24gYWNjdXJhY3kgcGxvdHMgcmFuayB0aGUgZW5zZW1ibGUgbWVtYmVycyBmcm9tIGxhcmdlc3QgdW5kZXJwcmVkaWN0aW9uIHRvIGxhcmdlc3Qgb3ZlcnByZWRpY3Rpb24gdXNpbmcgdGhlIHdhdmUwMCBwcmVkaWN0aW9ucy4gQSBwZXJmZWN0IHByZWRpY3Rpb24gd291bGQgYXBwZWFyIG9uIHRoZSBob3Jpem9udGFsICJ6ZXJvIiBsaW5lLgoKTWFueSBvZiB0aGUgd2F2ZTAxIHByZWRpY3Rpb25zIGFyZSBjbG9zZXIgdG8gdGhlIGhvcml6b250YWwgbGluZSwgYW5kIHRoZXJlZm9yZSBtb3JlIGFjY3VyYXRlIHByZWRpY3Rpb25zLiAgCgpOb25lIG9mIHRoZSBwcmVkaWN0aW9ucyBhcmUgb3V0c2lkZSB0aGUgdW5jZXJ0YWludHkgYm91bmRzLCB3aGljaCBzdWdnZXN0cyB0aGV5IGFyZSBvdmVyY29uc2VydmF0aXZlIChzaG91bGQgYmUgc21hbGxlcikuCgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTB9CgojcGRmKGZpbGUgPSAnZmlncy9rbWxvb3N0YXRzX1lfbGV2ZWwxYS5wZGYnLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMikKcGFyKG1mcm93ID0gYyg0LDEpLCBtYXIgPSBjKDMsNCwyLDIpLCBvbWEgPSBjKDQsNCw0LDAuMSkpCmZvcihpIGluIDE6bGVuZ3RoKGxvb2xpc3Rfa21fWV9sZXZlbDFhX2xldmVsMikpewogIAogIHkgPC0gWV9jb25zdF9sZXZlbDFhX3NjYWxlZFtsZXZlbDJfaXgsIGldCgogIGxvb18wMCA8LSBsb29saXN0X2ttX1lfbGV2ZWwxYV9sZXZlbDJbW2ldXQogIGxvb18wMSA8LSBsb29saXN0X2ttX1lfbGV2ZWwxYV93YXZlMDFfbGV2ZWwyW1tpXV0KICAKICBwcmVkZGlmZl93YXZlMDAgPC0geSAtIGxvb18wMCRtZWFuCiAgcHJlZGRpZmZfd2F2ZTAxIDwtIHkgLSBsb29fMDEkbWVhbgogIAogICAgIyByYW5rIGJ5IHRoZSBvcmlnaW5hbCB3YXZlIDAwIHByZWRpY3Rpb25zCiAgbG9vX3JhbmtfaXggPC0gc29ydChwcmVkZGlmZl93YXZlMDAgLCBpbmRleC5yZXR1cm4gPSBUUlVFKQogIAogICB5bGltIDwtIHJhbmdlKGMocHJlZGRpZmZfd2F2ZTAwW2xvb19yYW5rX2l4JGl4XSAtICgyKmxvb18wMCRzZFtsb29fcmFua19peCRpeF0pLAogICAgICAgICAgICAgICAgICAgcHJlZGRpZmZfd2F2ZTAwW2xvb19yYW5rX2l4JGl4XSArICgyKmxvb18wMCRzZFtsb29fcmFua19peCRpeF0pLAogICAgICAgICAgICAgICAgICAgcHJlZGRpZmZfd2F2ZTAxW2xvb19yYW5rX2l4JGl4XSAtICgyKmxvb18wMSRzZFtsb29fcmFua19peCRpeF0pLAogICAgICAgICAgICAgICAgICAgcHJlZGRpZmZfd2F2ZTAxW2xvb19yYW5rX2l4JGl4XSArICgyKmxvb18wMSRzZFtsb29fcmFua19peCRpeF0pCiAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgKQogICAKICAgcGxvdChwcmVkZGlmZl93YXZlMDBbbG9vX3JhbmtfaXgkaXhdLCB4bGFiID0gJycsIHlsYWIgPSAnJywgbWFpbiA9ICcnICwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwgMjU1KSwKICAgICAgICBwY2ggPSAxOSwgeWxpbSA9IHlsaW0pCiAgIAogICBhYmxpbmUoaCA9IDApCiAgIAogIGFycm93cyh4MCA9IDE6bGVuZ3RoKHkpLCB5MCA9IHByZWRkaWZmX3dhdmUwMFtsb29fcmFua19peCRpeF0gLSAoMipsb29fMDAkc2RbbG9vX3JhbmtfaXgkaXhdKSAgLCB4MSA9IDE6bGVuZ3RoKHkpICwgeTEgPSBwcmVkZGlmZl93YXZlMDBbbG9vX3JhbmtfaXgkaXhdICsgKDIqbG9vXzAwJHNkW2xvb19yYW5rX2l4JGl4XSksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsIDE1MCksICBhbmdsZSA9IDkwLCBjb2RlID0gMywgbGVuZ3RoID0gMC4wMykKICAgCiAgcG9pbnRzKHByZWRkaWZmX3dhdmUwMVtsb29fcmFua19peCRpeF0sIHhsYWIgPSAnJywgeWxhYiA9ICcnLCBtYWluID0gJycgLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLCAyNTUpLAogICAgICAgIHBjaCA9IDE5KQogIAogICAgYXJyb3dzKHgwID0gMTpsZW5ndGgoeSksIHkwID0gcHJlZGRpZmZfd2F2ZTAxW2xvb19yYW5rX2l4JGl4XSAtICgyKmxvb18wMSRzZFtsb29fcmFua19peCRpeF0pICAsIHgxID0gMTpsZW5ndGgoeSkgLCB5MSA9IHByZWRkaWZmX3dhdmUwMVtsb29fcmFua19peCRpeF0gKyAoMipsb29fMDEkc2RbbG9vX3JhbmtfaXgkaXhdKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMWNvbCwgMTUwKSwgYW5nbGUgPSA5MCwgY29kZSA9IDMsIGxlbmd0aCA9IDAuMDMpCiAgIAogICBtdGV4dChjb2xuYW1lcyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKVtpXSwgc2lkZSA9IDMsIGFkaiA9IDAsIGxpbmUgPSAxKQogIAoKfQoKCiByZXNldCgpCiBsZWdlbmQoJ3RvcGxlZnQnLCBwY2ggPSAxOSwgbGVnZW5kID0gYygnd2F2ZTAwJywgJ3dhdmUwMScpLCBjb2wgPSBjKHdhdmUwMGNvbCwgd2F2ZTAxY29sICksIGhvcml6ID0gVFJVRSkKCmBgYAoKCmBgYHtyfQoKbG9vc3RhdHNfa21fWV9sZXZlbDFhX3N1YiA8LSBsYXBwbHkoZml0X2xpc3RfY29uc3RfbGV2ZWwxYSwgRlVOID0ga21Mb29TdGF0c1N1YnNldCwgaXggPSBsZXZlbDJfaXgpCmxvb3N0YXRzX2ttX1lfbGV2ZWwxYV93YXZlMDFfc3ViIDwtIGxhcHBseShmaXRfbGlzdF9jb25zdF9sZXZlbDFhX3dhdmUwMSwgRlVOID0ga21Mb29TdGF0c1N1YnNldCwgaXggPSBsZXZlbDJfaXgpCgpgYGAKCkxvb2tpbmcgYXQgdGhlIHByb3BvcnRpb25hbCBtZWFuIGFic29sdXRlIGVycm9yIChwbWFlKSwgZXhwcmVzc2VkIGluIHBlcmNlbnQsIHdlIGNhbiBzZWUgdGhhdCBpdCBkb2Vzbid0IGltcHJvdmUgbXVjaCBmb3IgdGhlIHdob2xlIGVuc2VtYmxlLCBidXQgKmRvZXMqIGltcHJvdmUgc2lnbmlmaWNhbnRseSBmb3IgdGhlIHN1YnNldCBvZiBlbnNlbWJsZSBtZW1iZXJzIHRoYXQgZmFsbCB3aXRoaW4gQVcncyBjb25zdHJhaW50cyBmcm9tIHRoZSBmaXJzdCBlbnNlbWJsZSAobWFya2VkICJfc3ViIikuCgpgYGB7ciwgZWNobyA9IFRSVUV9CgpwbWFlX3dhdmUwMCA8LSBsYXBwbHkobG9vc3RhdHNfa21fWV9sZXZlbDFhLCBGVU4gPSBmdW5jdGlvbih4KSB4JHBtYWUgKQpwbWFlX3dhdmUwMSA8LSBsYXBwbHkobG9vc3RhdHNfa21fWV9sZXZlbDFhX3dhdmUwMSwgRlVOID0gZnVuY3Rpb24oeCkgeCRwbWFlICkKCnBtYWVfd2F2ZTAwX3N1YiA8LSBsYXBwbHkobG9vc3RhdHNfa21fWV9sZXZlbDFhX3N1YiwgRlVOID0gZnVuY3Rpb24oeCkgeCRwbWFlICkKcG1hZV93YXZlMDFfc3ViIDwtIGxhcHBseShsb29zdGF0c19rbV9ZX2xldmVsMWFfd2F2ZTAxX3N1YiwgRlVOID0gZnVuY3Rpb24oeCkgeCRwbWFlICkKCnBtYWVfdGFibGUgPC0gY2JpbmQocG1hZV93YXZlMDAsIHBtYWVfd2F2ZTAxLCBwbWFlX3dhdmUwMF9zdWIsIHBtYWVfd2F2ZTAxX3N1YikKCnByaW50KHBtYWVfdGFibGUpCgpgYGAKCiMgQ29tcGFyaW5nIGF0bW9zcGhlcmljIGdyb3d0aCBpbiB3YXZlMDAsIHdhdmUwMSBhbmQgb2JzZXJ2YXRpb25zCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQgPC0gcmVhZF9leGNlbCgnR2xvYmFsX0NhcmJvbl9CdWRnZXRfMjAyMHYxLjAueGxzeCcsIHNoZWV0ID0gIkhpc3RvcmljYWwgQnVkZ2V0Iiwgc2tpcCA9IDE1LCBuX21heCA9IDI3MCkKCnJlc2lkIDwtIGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRgb2NlYW4gc2lua2AgLSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYGZvc3NpbCBlbWlzc2lvbnMgZXhjbHVkaW5nIGNhcmJvbmF0aW9uYAoKcmVzaWRfaXggPC0gd2hpY2goaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JFllYXIgJWluJSB5ZWFycykKCm5lZ19hZyA8LSBzd2VlcChuYnBfZW5zLCAyLCByZXNpZFtyZXNpZF9peF0sIEZVTiA9ICcrJykKCgptYXRwbG90KHllYXJzLCB0KC1uZWdfYWcpLCB0eXBlID0gJ2wnLCBsdHkgPSAnc29saWQnLHlsaW0gPSBjKDAsIDgpLCBjb2wgPSB3YXZlMDBjb2wsIAogICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnYXRtb3NwaGVyaWMgZ3Jvd3RoJywgeGxhYiA9ICcnLAogICAgICAgICBidHkgPSAnbicsIHhsaW0gPSBjKDE5NjAsIDIwMjApKQoKbGluZXMoaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JFllYXIsIGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRgYXRtb3NwaGVyaWMgZ3Jvd3RoYCwgY29sID0gJ3JlZCcpCgoKYGBgCgoKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDZ9CgpvYnNjb2wgPSAncHVycGxlJwp3YXZlMDFfbGV2ZWwyY29sIDwtICdnb2xkJwoKYWcgPC0gbWF0cml4KG5yb3cgPSBucm93KG5icF9lbnMpLCBuY29sID0gbmNvbChuYnBfZW5zKSkKCmFnMDEgPC0gbWF0cml4KG5yb3cgPSBucm93KG5icF9lbnNfd2F2ZTAxKSwgbmNvbCA9IG5jb2wobmJwX2Vuc193YXZlMDEpKQoKZm9yKGkgaW4gMTpucm93KG5icF9lbnMpKXsKICAKYWdbaSwgXSA8LSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYGZvc3NpbCBlbWlzc2lvbnMgZXhjbHVkaW5nIGNhcmJvbmF0aW9uYFtyZXNpZF9peF0gLSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYG9jZWFuIHNpbmtgW3Jlc2lkX2l4XSAtICBuYnBfZW5zW2ksIF0KICAKfQoKCmZvcihpIGluIDE6bnJvdyhuYnBfZW5zX3dhdmUwMSkpewphZzAxW2ksIF0gPC0gaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBmb3NzaWwgZW1pc3Npb25zIGV4Y2x1ZGluZyBjYXJib25hdGlvbmBbcmVzaWRfaXhdIC0gaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JGBvY2VhbiBzaW5rYFtyZXNpZF9peF0gLSAgbmJwX2Vuc193YXZlMDFbaSwgXQogIAp9CgphZ19zdGFuIDwtIGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRgZm9zc2lsIGVtaXNzaW9ucyBleGNsdWRpbmcgY2FyYm9uYXRpb25gW3Jlc2lkX2l4XSAtIGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRgb2NlYW4gc2lua2BbcmVzaWRfaXhdIC0gIG5icF9zdGFuCgojcGRmKHdpZHRoID0gOCwgaGVpZ2h0ID0gNiwgZmlsZSA9ICdhZy5wZGYnKQptYXRwbG90KHllYXJzLCB0KGFnKSwgdHlwZSA9ICdsJywgbHR5ID0gJ3NvbGlkJyx5bGltID0gYygtMiwgMTApLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAwY29sLDEwMCksIAogICAgICAgICB5bGFiID0gJ0d0QycsIG1haW4gPSAnYXRtb3NwaGVyaWMgZ3Jvd3RoJywgeGxhYiA9ICcnLAogICAgICAgICBidHkgPSAnbicsIHhsaW0gPSBjKDE5NjAsIDIwMTMpKQoKbWF0bGluZXMoeWVhcnMsIHQoYWcwMSksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFjb2wsMTAwKSwgbHR5ID0gJ3NvbGlkJykKCm1hdGxpbmVzKHllYXJzLCB0KGFnMDFbbGV2ZWwyX2l4X3dhdmUwMSwgXSksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDFfbGV2ZWwyY29sLCAxMDApICwgbHR5ID0gJ3NvbGlkJykKCmxpbmVzKGhpc3RvcmljYWxfY2FyYm9uX2J1ZGdldCRZZWFyLCBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYGF0bW9zcGhlcmljIGdyb3d0aGAsIGNvbCA9IG9ic2NvbCwgbHdkID0yKQoKbGluZXMoeWVhcnMsIGFnX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx3ZCA9MikKCgpsZWdlbmQoJ3RvcGxlZnQnLCBsZWdlbmQgPSBjKCd3YXZlMDAnLCAnd2F2ZTAxJywgJ3dhdmUwMSBjb25zdHJhaW5lZCcsICdzdGFuZGFyZCcsICdHQ0InKSwgY29sID0gYyh3YXZlMDBjb2wsIHdhdmUwMWNvbCwgd2F2ZTAxX2xldmVsMmNvbCwgc3RhbmNvbCwgb2JzY29sKSwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMikKI2Rldi5vZmYoKQoKYGBgCgoKIyMgQW5keSBhc2tzIC0gd2hhdCBjb25zdHJhaW50IGRvZXMgdGhhdCBnaXZlIHVzIGluIGN1bXVsYXRpdmUgTkJQPwoKYGBge3IsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA3fQoKY3VtdWxhdGl2ZV9uYnBfZW5zIDwtIHQoYXBwbHkobmJwX2VucywgMSwgY3Vtc3VtKSkKCmN1bXVsYXRpdmVfbmJwX2Vuc193YXZlMDEgPC0gdChhcHBseShuYnBfZW5zX3dhdmUwMSwgMSwgY3Vtc3VtKSkKCmN1bXVsYXRpdmVfbmJwX3N0YW4gPC0gY3Vtc3VtKG5icF9zdGFuKQoKbWF0cGxvdCh5ZWFycywgdChjdW11bGF0aXZlX25icF9lbnMpLCB0eXBlID0gJ2wnLCBsdHkgPSAnc29saWQnLHlsaW0gPSBjKC0xMzAsIDI1MCksIGNvbCA9IG1ha2VUcmFuc3BhcmVudCh3YXZlMDBjb2wsMTUwKSwgCiAgICAgICAgIHlsYWIgPSAnR3RDJywgbWFpbiA9ICdjdW11bGF0aXZlIE5CUCcsIHhsYWIgPSAnJywKICAgICAgICAgYnR5ID0gJ24nLCB4bGltID0gYygxODUwLCAyMDIwKSkKCm1hdGxpbmVzKHllYXJzLCB0KGN1bXVsYXRpdmVfbmJwX2Vuc193YXZlMDEpLAogICAgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxY29sLDE1MCksCiAgICAgICAgIGx0eSA9ICdzb2xpZCcpCgptYXRsaW5lcyh5ZWFycywgdChjdW11bGF0aXZlX25icF9lbnNfd2F2ZTAxW2xldmVsMl9peF93YXZlMDEsIF0pLAogICAgICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQod2F2ZTAxX2xldmVsMmNvbCwgMTUwKSwKICAgICAgICAgbHR5ID0gJ3NvbGlkJykKCgpsaW5lcyh5ZWFycywgY3VtdWxhdGl2ZV9uYnBfc3RhbiwgY29sID0gbWFrZVRyYW5zcGFyZW50KHN0YW5jb2wsIDIwMCksIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDEuNSApCgoKbGVnZW5kKCd0b3BsZWZ0JywgbGVnZW5kID0gYygnd2F2ZTAwJywgJ3dhdmUwMScsICd3YXZlMDEgY29uc3RyYWluZWQnLCAnc3RhbmRhcmQnKSwgY29sID0gYyh3YXZlMDBjb2wsIHdhdmUwMWNvbCwgd2F2ZTAxX2xldmVsMmNvbCwgc3RhbmNvbCksIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDEuNSkKCmBgYAoKIyMgRWRkeSBzdWdnZXN0cyBtZWFzdXJpbmcgY3VtdWxhdGl2ZSBOQlAgYWdhaW5zdCBhdG1vc3BoZXJpYyBncm93dGggcmF0ZQoKYGBge3J9CgojIEN1bXVsYXRpdmUgTkJQIGF0IHRoZSBlbmQgb2YgdGhlIHJ1bgpjbmJwX21vZGVybl9lbnMgPC0gYXBwbHkoY3VtdWxhdGl2ZV9uYnBfZW5zWywgMTM1OjE2NF0sIDEsIG1lYW4pCgpjbmJwX21vZGVybl93YXZlMDEgPC0gYXBwbHkoY3VtdWxhdGl2ZV9uYnBfZW5zX3dhdmUwMVssIDEzNToxNjRdLCAxLCBtZWFuKQoKY25icF9tb2Rlcm5fc3RhbiA8LSBtZWFuKGN1bXVsYXRpdmVfbmJwX3N0YW5bMTM1OjE2NF0pCgoKCmBgYAoKQ2FsY3VsYXRlIHRoZSBhdG1vc3BoZXJpYyBncm93dGggcmF0ZSBvZiAxOTg0LSAyMDEzIHVzaW5nIGEgc2ltcGxlIGxpbmVhciBmaXQKCmBgYHtyfQojIGFnciA9IGF0bW9zcGhlcmljIGdyb3d0aCByYXRlCiMgYWdpYXYgPSBhdG1vc3BoZXJpYyBncm93dGggaW50ZXJhbm51YWwgdmFyaWFiaWxpdHkKCmFncl9tb2Rlcm5fZW5zIDwtIHJlcChOQSwgbGVuZ3RoID0gbnJvdyhhZykpCmFnaWF2X21vZGVybl9lbnMgPC0gcmVwKE5BLCBsZW5ndGggPSBucm93KGFnKSkKCmZvcihpIGluIDE6bnJvdyhhZykpewogIAogIGRhdCA8LSBkYXRhLmZyYW1lKHQgPTE6MzAsIGFnID0gIGFnW2ksIDEzNToxNjRdKQogIGZpdCA8LSBsbShhZyB+IHQsIGRhdGEgPSBkYXQpCiAgYWdyX21vZGVybl9lbnNbaV0gPC0gY29lZihmaXQpWyd0J10KICBhZ2lhdl9tb2Rlcm5fZW5zW2ldIDwtIHNkKGZpdCRyZXNpZHVhbHMpCn0KCgphZ3JfbW9kZXJuX3dhdmUwMSA8LSByZXAoTkEsIGxlbmd0aCA9IG5yb3coYWcwMSkpCmFnaWF2X21vZGVybl93YXZlMDEgPC0gcmVwKE5BLCBsZW5ndGggPSBucm93KGFnMDEpKQoKZm9yKGkgaW4gMTpucm93KGFnMDEpKXsKICAKICBkYXQgPC0gZGF0YS5mcmFtZSh0ID0xOjMwLCBhZyA9ICBhZzAxW2ksIDEzNToxNjRdKQogIGZpdCA8LSBsbShhZyB+IHQsIGRhdGEgPSBkYXQpCiAgYWdyX21vZGVybl93YXZlMDFbaV0gPC0gY29lZihmaXQpWyd0J10KICBhZ2lhdl9tb2Rlcm5fd2F2ZTAxW2ldIDwtIHNkKGZpdCRyZXNpZHVhbHMpCn0KCmRhdCA8LSBkYXRhLmZyYW1lKHQgPTE6MzAsIGFnID0gIGFnX3N0YW5bMTM1OjE2NF0pCmFncl9zdGFuX2ZpdCA8LSBsbShhZyB+IHQsIGRhdGEgPSBkYXQpIAphZ3Jfc3RhbiA8LSBjb2VmKGFncl9zdGFuX2ZpdClbJ3QnXQphZ2lhdl9zdGFuIDwtIHNkKGFncl9zdGFuX2ZpdCRyZXNpZHVhbHMpCiAgCiAgCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA3fQoKcGxvdChhZ3JfbW9kZXJuX2VucywgY25icF9tb2Rlcm5fZW5zLCAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwxNTApLAogICAgIHlsaW0gPSBjKC0xMjAsMjIwKSwgcGNoID0gMTksCiAgICAgeGxhYiA9ICdBdG1vc3BoZXJpYyBncm93dGggcmF0ZSAxOTg0LTIwMTQnLAogICAgIHlsYWIgPSAnQ3VtdWxhdGl2ZSBOQlAgZnJvbSBwcmVpbmR1c3RyaWFsIGJ5IDE5ODQtMjAxMycKICAgICApCgpjbmJwX3dhdmUwMV9jb2xzIDwtIHJlcCh3YXZlMDFjb2wsIGxlbmd0aChjbmJwX21vZGVybl93YXZlMDEpKQpjbmJwX3dhdmUwMV9jb2xzW2xldmVsMl9peF93YXZlMDFdIDwtIHdhdmUwMV9sZXZlbDJjb2wgCgpwb2ludHMoIGFncl9tb2Rlcm5fd2F2ZTAxLGNuYnBfbW9kZXJuX3dhdmUwMSwgY29sID0gbWFrZVRyYW5zcGFyZW50KGNuYnBfd2F2ZTAxX2NvbHMsIDE1MCksIHBjaCA9IDE5KQpwb2ludHMoIGFncl9zdGFuLGNuYnBfbW9kZXJuX3N0YW4sIGNvbCA9IHN0YW5jb2wsIHBjaCA9IDE5LCBjZXggPSAxLjUpCgpsZWdlbmQoJ3RvcGxlZnQnLCBsZWdlbmQgPSBjKCd3YXZlMDAnLCAnd2F2ZTAxJywgJ3dhdmUwMSBjb25zdHJhaW5lZCcsICdzdGFuZGFyZCcpLCBjb2wgPSBjKHdhdmUwMGNvbCwgd2F2ZTAxY29sLCB3YXZlMDFfbGV2ZWwyY29sLCBzdGFuY29sKSwgcGNoID0gMTkpCgpwcmludChjKCdjb3JyZWxhdGlvbiBhZ3IgdnMgY25icCAoYWxsIG1lbWJlcnMpJywgY29yKGFncl9tb2Rlcm5fZW5zLCBjbmJwX21vZGVybl9lbnMpKSkKCnByaW50KGMoJ2NvcnJlbGF0aW9uIGFnciB2cyBjbmJwICh3YXZlMDEpJywgY29yKGFncl9tb2Rlcm5fd2F2ZTAxLCBjbmJwX21vZGVybl93YXZlMDEpKSkKCmZpdF93YXZlMDAgPC0gbG0oYWdyX21vZGVybl9lbnMgfiBjbmJwX21vZGVybl9lbnMpCmZpdF93YXZlMDEgPC0gbG0oYWdyX21vZGVybl93YXZlMDEgfiBjbmJwX21vZGVybl93YXZlMDEpCgpwcmludChzdW1tYXJ5KGZpdF93YXZlMDApKQpwcmludChzdW1tYXJ5KGZpdF93YXZlMDEpKQoKYGBgCkludGVyYW5udWFsIHZhcmlhYmlsaXR5IGFuZCBjdW11bGF0aXZlIE5CUCAgCgooY29ycmVsYXRpb25zIGFyZSBjbG9zZSB0byB6ZXJvLCBlc3BlY2lhbGx5IGluIHRoZSBsYXRlciB3YXZlKQoKCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gN30KCnBsb3QoYWdpYXZfbW9kZXJuX2VucywgY25icF9tb2Rlcm5fZW5zLCAgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwxNTApLAogICAgIHlsaW0gPSBjKC0xMjAsMjIwKSwgcGNoID0gMTksCiAgICAgeGxhYiA9ICdBdG1vc3BoZXJpYyBncm93dGggSUFWIDE5ODQtMjAxNCcsCiAgICAgeWxhYiA9ICdDdW11bGF0aXZlIE5CUCBmcm9tIHByZWluZHVzdHJpYWwgYnkgMTk4NC0yMDEzJwogICAgICkKCnBvaW50cyggYWdpYXZfbW9kZXJuX3dhdmUwMSwgY25icF9tb2Rlcm5fd2F2ZTAxLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoY25icF93YXZlMDFfY29scywgMTUwKSwgcGNoID0gMTkpCnBvaW50cyggYWdpYXZfc3RhbixjbmJwX21vZGVybl9zdGFuLCBjb2wgPSBzdGFuY29sLCBwY2ggPSAxOSwgY2V4ID0gMS41KQoKbGVnZW5kKCdib3R0b21yaWdodCcsIGxlZ2VuZCA9IGMoJ3dhdmUwMCcsICd3YXZlMDEnLCAnd2F2ZTAxIGNvbnN0cmFpbmVkJywgJ3N0YW5kYXJkJyksIGNvbCA9IGMod2F2ZTAwY29sLCB3YXZlMDFjb2wsIHdhdmUwMV9sZXZlbDJjb2wsIHN0YW5jb2wpLCBwY2ggPSAxOSkKCmBgYAoKCmBgYHtyfQoKCnByaW50KGNvcihhZ2lhdl9tb2Rlcm5fZW5zLCBjbmJwX21vZGVybl9lbnMpKQpwcmludChjb3IoIGFnaWF2X21vZGVybl93YXZlMDEsIGNuYnBfbW9kZXJuX3dhdmUwMSkpCgpgYGAKCgojIyBIb3cgY2xvc2UgY2FuIHdlIGdldCB0aGUgbW9kZWwgdG8gcmVhbGl0eT8KClVzaW5nIEF0bW9zcGhlcmljIEdyb3d0aCBSYXRlIGFzIGFuIGV4YW1wbGUsIGhvdyBjbG9zZSBjYW4gd2UgZ2V0IHRoZSBtb2RlbCB0byBvYnNlcnZhdGlvbnM/IENhbiB3ZSBkbyBiZXR0ZXIgdGhhbiBzdGFuZGFyZD8gV2hhdCBhcmUgdGhlIHRyYWRlIG9mZnMgb2YgZG9pbmcgc28/IEhvdyBkb2VzIGdldHRpbmcgY2xvc2UgaW4gQUdSIGFmZmVjdCBwZXJmb3JtYW5jZSBpbiBvdGhlciBvdXRwdXRzPwoKYGBge3J9CiMgRGVmaW5lIHRoZSBvYnNlcnZlZCBhdG1vc3BoZXJpYyBncm93dGggcmF0ZS4KCmFnX29icyA8LSBoaXN0b3JpY2FsX2NhcmJvbl9idWRnZXQkYGF0bW9zcGhlcmljIGdyb3d0aGBbd2hpY2goaGlzdG9yaWNhbF9jYXJib25fYnVkZ2V0JFllYXIgJWluJSB5ZWFycyldCgojIE1vZGVsIGRlcGFydHVyZSBmcm9tIG9ic2VydmF0aW9ucyBvZiBhdG1vc3BoZXJpYyBncm93dGgKYWdfZXJyIDwtIHN3ZWVwKGFnLCAyLCBhZ19vYnMsIEZVTiA9ICctJykKYWcwMV9lcnIgPC0gc3dlZXAoYWcwMSwgMiwgYWdfb2JzLCBGVU4gPSAnLScpCgoKbG9uZ19tb2Rlcm5feWVhcnMgPC0gMTk2MDoyMDEzCmFnX21vZGVybl9peCA8LSB3aGljaCggeWVhcnMgJWluJSBsb25nX21vZGVybl95ZWFycykKCmFnX2Vycl9tb2Rlcm4gPC0gYWdfZXJyWywgYWdfbW9kZXJuX2l4XQphZzAxX2Vycl9tb2Rlcm4gPC0gYWcwMV9lcnJbLCBhZ19tb2Rlcm5faXhdCgoKYWdfZXJyX3N0YW4gPC0gYWdfc3RhbiAtIGFnX29icwphZ19lcnJfc3Rhbl9tb2Rlcm4gPC0gYWdfZXJyX3N0YW5bYWdfbW9kZXJuX2l4XQoKYGBgCgpgYGB7cn0KbWF0cGxvdChsb25nX21vZGVybl95ZWFycywgdChhZ19lcnJfbW9kZXJuKSwgdHlwZSA9ICdsJywgbHR5ID0gJ3NvbGlkJywgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMGNvbCwgMTAwKSwgbWFpbiA9ICdBbW9zcGhlcmljIEdyb3d0aCBFcnJvcicpCgplcnJjb2wgPC0gcmVwKHdhdmUwMWNvbCwgbnJvdyhhZzAxKSkKI2VycmNvbFtsZXZlbDJfaXhfd2F2ZTAxXSA8LSB3YXZlMDFfbGV2ZWwyY29sCm1hdGxpbmVzKGxvbmdfbW9kZXJuX3llYXJzLCB0KGFnMDFfZXJyX21vZGVybiksIGNvbCA9IG1ha2VUcmFuc3BhcmVudChlcnJjb2wsIDEwMCksIGx0eSA9ICdzb2xpZCcpCm1hdGxpbmVzKGxvbmdfbW9kZXJuX3llYXJzLCB0KGFnMDFfZXJyX21vZGVybltsZXZlbDJfaXhfd2F2ZTAxLCBdKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KHdhdmUwMV9sZXZlbDJjb2wsMTAwKSwgbHR5ID0gJ3NvbGlkJykKCmxpbmVzKGxvbmdfbW9kZXJuX3llYXJzLCBhZ19lcnJfc3Rhbl9tb2Rlcm4sIGNvbCA9IHN0YW5jb2wpCgpsZWdlbmQoJ2JvdHRvbWxlZnQnLCBsZWdlbmQgPSBjKCd3YXZlMDAnLCAnd2F2ZTAxJywgJ3dhdmUwMSBjb25zdHJhaW5lZCcsICdzdGFuZGFyZCcpLCBjb2wgPSBjKHdhdmUwMGNvbCwgd2F2ZTAxY29sLCB3YXZlMDFfbGV2ZWwyY29sLCBzdGFuY29sKSwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMS41KQoKYWJsaW5lKGg9MCkKCmBgYAoKCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gN30KCiMgQXRtb3NwaGVyaWMgZ3Jvd3RoIG1lYW4gZXJyb3IKYWdfbW9kZXJuX21lIDwtIGFwcGx5KGFnX2Vycl9tb2Rlcm4sMSxtZWFuKQphZzAxX21vZGVybl9tZSA8LSBhcHBseShhZzAxX2Vycl9tb2Rlcm4sMSwgbWVhbikKYWdfbW9kZXJuX21lX3N0YW4gPC0gbWVhbihhZ19lcnJfc3RhblthZ19tb2Rlcm5faXhdKQoKCiMgTWVhbiBhYnNvbHV0ZSBlcnJvcgphZ19tb2Rlcm5fbWFlIDwtIGFwcGx5KGFicyhhZ19lcnJfbW9kZXJuKSwxLG1lYW4pCmFnMDFfbW9kZXJuX21hZSA8LSBhcHBseShhYnMoYWcwMV9lcnJfbW9kZXJuKSwxLCBtZWFuKQphZ19tb2Rlcm5fbWFlX3N0YW4gPC0gbWVhbihhYnMoYWdfZXJyX3N0YW5bYWdfbW9kZXJuX2l4XSkpCgoKCiMgUm9vdCBtZWFuIHNxdWFyZSBlcnJvcgphZ19tb2Rlcm5fcm1zZSA8LSBhcHBseShhZ19lcnJfbW9kZXJuLDEsIGZ1bmN0aW9uKHgpIHNxcnQobWVhbih4XjIpKSkKYWcwMV9tb2Rlcm5fcm1zZSA8LSBhcHBseShhZzAxX2Vycl9tb2Rlcm4sMSwgZnVuY3Rpb24oeCkgc3FydChtZWFuKHheMikpKQphZ19tb2Rlcm5fcm1zZV9zdGFuIDwtIHNxcnQobWVhbihhZ19lcnJfc3RhblthZ19tb2Rlcm5faXhdXjIpKQoKIyBUaGVyZSBhcmUgc29tZSBiaWcgb3V0bGllcnMgCm91dGxpZXJfaXhfd2F2ZTAxIDwtIHdoaWNoKGFicyhhZzAxX21vZGVybl9tZSApPiAxMDApCgpwYXIobWZyb3cgPSBjKDIsMSkpCmhpc3QoYWdfbW9kZXJuX21lLCBtYWluID0gJ0F0bW9zcGhlcmljIGdyb3d0aCBtZWFuIGVycm9yJywgY29sID0gd2F2ZTAwY29sLCB4bGltID0gYygtMywgMykpCnJ1ZyhhZ19tb2Rlcm5fbWVfc3RhbiwgY29sID0gc3RhbmNvbCwgbHdkID0gMikKaGlzdChhZzAxX21vZGVybl9tZVstb3V0bGllcl9peF93YXZlMDFdLCBjb2wgPSB3YXZlMDFjb2wsIHhsaW0gPSBjKC0zLDMpLCBtYWluID0gJycpCnJ1ZyhhZ19tb2Rlcm5fbWVfc3RhbiwgY29sID0gc3RhbmNvbCwgbHdkID0gMikKCnBhcihtZnJvdyA9IGMoMiwxKSkKaGlzdChhZ19tb2Rlcm5fbWFlLCBtYWluID0gJ0F0bW9zcGhlcmljIGdyb3d0aCBtZWFuIGFic29sdXRlIGVycm9yJywgY29sID0gd2F2ZTAwY29sLCB4bGltID0gYygwLCAzKSkKcnVnKGFnX21vZGVybl9tYWVfc3RhbiwgY29sID0gc3RhbmNvbCwgbHdkID0gMikKaGlzdChhZzAxX21vZGVybl9tYWVbLW91dGxpZXJfaXhfd2F2ZTAxXSwgY29sID0gd2F2ZTAxY29sLCBsd2QgPSAyLCB4bGltID0gYygwLDMpLCBtYWluID0gJycpCnJ1ZyhhZ19tb2Rlcm5fbWFlX3N0YW4sIGNvbCA9IHN0YW5jb2wsIGx3ZCA9IDIpCgpwYXIobWZyb3cgPSBjKDIsMSkpCmhpc3QoYWdfbW9kZXJuX3Jtc2UsIHhsaW0gPSBjKDAsMyksIGNvbCA9IHdhdmUwMGNvbCwgbWFpbiA9ICdBdG1vc3BoZXJpYyBncm93dGggUk1TRScpCnJ1ZyhhZ19tb2Rlcm5fcm1zZV9zdGFuLCBjb2wgPSBzdGFuY29sLCBsd2QgPSAyICkKaGlzdChhZzAxX21vZGVybl9ybXNlWy1vdXRsaWVyX2l4X3dhdmUwMV0sIGNvbCA9IHdhdmUwMWNvbCwgeGxpbSA9IGMoMCwzKSwgbWFpbiA9ICcnKQpydWcoYWdfbW9kZXJuX3Jtc2Vfc3RhbiwgY29sID0gc3RhbmNvbCwgbHdkID0gMiApCgoKCmBgYAoKCldlJ3ZlIGVzdGFibGlzaGVkIHRoYXQgbW9zdCBvZiB0aGUgb3JpZ2luYWwgZW5zZW1ibGUgaGF2ZSBhbiBNRS9NQUUvUk1TRSBsYXJnZXIgdGhhbiB0aGUgc3RhbmRhcmQgcnVuLiBNb3JlIChidXQgZmV3KSBvZiB0aGUgd2F2ZTAxIHBlcmZvcm0gYmV0dGVyIHRoYW4gc3RhbmRhcmQuIAoKYGBge3J9CgoKYmV0dGVyX2l4X2FnX3Jtc2UgPC0gd2hpY2goYWdfbW9kZXJuX3Jtc2UgPCBhZ19tb2Rlcm5fcm1zZV9zdGFuKQpiZXR0ZXJfaXhfYWcwMV9ybXNlIDwtIHdoaWNoKGFnMDFfbW9kZXJuX3Jtc2UgPCBhZ19tb2Rlcm5fcm1zZV9zdGFuKQoKClhfYmV0dGVyX2FnIDwtIHJiaW5kKFhbYmV0dGVyX2l4X2FnX3Jtc2UsIF0sIFhfd2F2ZTAxX3RyYWluW2JldHRlcl9peF9hZzAxX3Jtc2UsIF0pCgpgYGAKCgpBIG1hcCBvZiB0aGUgMkQgcHJvamVjdGlvbnMgb2YgcGFyYW1ldGVyIHNwYWNlIHdoZXJlIHRoZSBlbnNlbWJsZSBtZW1iZXIgcGVyZm9ybXMgYmV0dGVyIHRoYW4gc3RhbmRhcmQuICAKClRoZSBibHVlIHBhcnQgaXMgdGhlIGZpcnN0IHdhdmUsIGFuZCBub3Qgc3ViamVjdCB0byBjb25zdHJhaW50IHNvIG1heSBiZSByZW1vdmVkIGluIHRoZSBzZWNvbmQgd2F2ZSAod2F2ZTAxKS4KCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KCmJldHRlcl9peCA8LSAxOm5yb3coWF9iZXR0ZXJfYWcpCgpiZXR0ZXJfY29scyA8LSBjKHJlcCh3YXZlMDBjb2wsIGxlbmd0aChiZXR0ZXJfaXhfYWdfcm1zZSkpLCByZXAod2F2ZTAxY29sLGxlbmd0aChiZXR0ZXJfaXhfYWcwMV9ybXNlKSkpCgpwYWlycyhYX2JldHRlcl9hZywKICAgICAgY29sID0gYmV0dGVyX2NvbHMsCiAgICAgIGdhcCA9IDAsCiAgICAgIHhsaW0gPSBjKDAsMSksIHlsaW0gPSBjKDAsMSksCiAgICAgIHBjaCA9IDE5LAogICAgICBjZXg9IDAuOCwKICAgICAgbG93ZXIucGFuZWwgPSBOVUxMKQoKCgpgYGAKCiMjIEJ1aWxkIGVtdWxhdG9ycyBhbmQgZmluZCBwYXJ0cyBvZiBwYXJhbWV0ZXIgc3BhY2UgdGhhdCB0byBiZXR0ZXIgdGhhbiBzdGFuZGFyZCBhdCBhdG1vc3BoZXJpYyBncm93dGguCgpIYXZpbmcgdHJvdWJsZSBmaXR0aW5nIFJNU0UsIHRvIHRyeWluZyBtZWFuIGVycm9yLgoKV2h5IGlzIHRoZXJlIGFuIG9kZCBjb2xsZWN0aW9uIGF0IGp1c3QgdW5kZXIgMT8KCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyfQojIHRoZXJlIGFyZSAxMDAgd2F2ZTAxIGVuc2VtYmxlIG1lbWJlcnMgdGhhdCBwYXNzIHRoZSBsZXZlbCAyIGNvbnN0cmFpbnN0Cmxlbmd0aChsZXZlbDJfaXhfd2F2ZTAxKQoKYmV0dGVyX2l4X2FnX21lIDwtIHdoaWNoKGFicyhhZ19tb2Rlcm5fbWUpIDwgYWJzKGFnX21vZGVybl9tZV9zdGFuKSkKCiMgVGhlcmUgYXJlIDEwNCB0aGF0IGhhdmUgYSBzbWFsbGVyIG1lYW4gZXJyb3IgdGhhbiBzdGFuZGFyZApiZXR0ZXJfaXhfYWcwMV9tZSA8LSB3aGljaChhYnMoYWcwMV9tb2Rlcm5fbWUpIDwgYWJzKGFnX21vZGVybl9tZV9zdGFuKSkKCiMgVGhlcmUgYXJlIG9ubHkgNDEgdGhhdCBwYXNzIGxldmVsIDIgQU5EIGhhdmUgc21hbGxlciBlcnJvciB0aGFuIHN0YW5kYXJkCmxldmVsMl9hbmRfYmV0dGVyX2FnMDFfaXggPC0gaW50ZXJzZWN0KGxldmVsMl9peF93YXZlMDEsIGJldHRlcl9peF9hZzAxX21lKQoKYGBgCgpUaGlzIG5leHQgcGFpcnMgcGxvdCBsb29rcyBhdCBhbGwgdGhlIGVuc2VtYmxlIG1lbWJlcnMgdGhhdCBoYXZlIGEgYmV0dGVyIG1lYW4gYXRtb3NwaGVyaWMgZ3Jvd3RoIGVycm9yIHRoYW4gc3RhbmRhcmQuCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KWF9iZXR0ZXJfYWcgPC0gcmJpbmQoWFtiZXR0ZXJfaXhfYWdfbWUsIF0sIFhfd2F2ZTAxX3RyYWluW2JldHRlcl9peF9hZzAxX21lLCBdKQoKYmV0dGVyX2l4IDwtIDE6bnJvdyhYX2JldHRlcl9hZykKCmJldHRlcl9jb2xzIDwtIGMobWFrZVRyYW5zcGFyZW50KHJlcCh3YXZlMDBjb2wsIGxlbmd0aChiZXR0ZXJfaXhfYWdfbWUpKSwgMTAwKSwgbWFrZVRyYW5zcGFyZW50KHJlcCh3YXZlMDFjb2wsbGVuZ3RoKGJldHRlcl9peF9hZzAxX21lKSksIDEwMCkpCgpwYWlycyhYX2JldHRlcl9hZywKICAgICAgY29sID0gYmV0dGVyX2NvbHMsCiAgICAgIGdhcCA9IDAsCiAgICAgIHhsaW0gPSBjKDAsMSksIHlsaW0gPSBjKDAsMSksCiAgICAgIHBjaCA9IDE5LAogICAgICBjZXg9IDAuOCwKICAgICAgbG93ZXIucGFuZWwgPSBOVUxMKQpgYGAKClRoaXMgbmV4dCBwbG90IGxvb2tzIGF0IGFsbCB0aGUgZW5zZW1ibGUgbWVtYmVycyB0aGF0IGhhdmUgYSBiZXR0ZXIgbWVhbiBhdG1vc3BoZXJpYyBncm93dGggZXJyb3IgdGhhbiBzdGFuZGFyZCBBTkQgcGFzcyB0aGUgbGV2ZWwgMiBjb25zdHJhaW50cy4gIAoKVGhlIG51bWJlciBpcyBzbWFsbCAoNDEvMzAwKSwgYnV0IHRoZSBlbnNlbWJsZSBtZW1iZXJzIHNlZW0gc3ByZWFkIGFjcm9zcyBwYXJhbWV0ZXIgc3BhY2UuCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KClhfbGV2ZWwyX2FuZF9iZXR0ZXJfYWcwMSA8LSBYW2xldmVsMl9hbmRfYmV0dGVyX2FnMDFfaXgsIF0KcGFpcnMoWF9sZXZlbDJfYW5kX2JldHRlcl9hZzAxLAogICAgICBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoJ2JsYWNrJywgMTUwKSwKICAgICAgZ2FwID0gMCwKICAgICAgeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSwKICAgICAgcGNoID0gMTksCiAgICAgIGNleD0gMC44LAogICAgICBsb3dlci5wYW5lbCA9IE5VTEwpCgpgYGAKCgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KbGlicmFyeShEaWNlS3JpZ2luZykKCmFnMDFfb3V0bGllcl9peCA8LSB3aGljaChhZzAxX21vZGVybl9tZSA+IDEwKQoKZml0X2FnX21lIDwtIGttKH4uLCBkZXNpZ24gPSBYX3dhdmUwMV90cmFpblstYWcwMV9vdXRsaWVyX2l4LCBdLCByZXNwb25zZSA9IGFnMDFfbW9kZXJuX21lWy1hZzAxX291dGxpZXJfaXhdLCAKICAgICAgICAgICAgICAgICAgY29udHJvbCA9IGxpc3QobWF4aXQgPSAyMDApKQoKYGBgCgpgYGB7cn0KCnBsb3QoZml0X2FnX21lKQoKCgoKYGBgCgpgYGB7cn0KbGlicmFyeShlbXRvb2xzKQpsaWJyYXJ5KGltcHRvb2xzKQpsaWJyYXJ5KHZpenRvb2xzKQoKbnVuaWYgPC0gMTAwMDAwClhfdW5pZiA8LSBzYW1wX3VuaWYobnVuaWYsIG1pbnMgPSByZXAoMCwzMiksIG1heGVzID0gcmVwKDEsIDMyKSkKY29sbmFtZXMoWF91bmlmKSA8LSBjb2xuYW1lcyhYKQoKcHJlZF91bmlmX2FnIDwtIHByZWRpY3Qua20oZml0X2FnX21lLCBuZXdkYXRhID0gWF91bmlmLCB0eXBlID0gJ1VLJyApIAoKIyBLZWVwcyBhYm91dCAyMCUKWF9rZXB0X2l4IDwtIHdoaWNoKGFicyhwcmVkX3VuaWZfYWckbWVhbikgPCBhYnMoYWdfbW9kZXJuX21lX3N0YW4pKQoKCihsZW5ndGgoWF9rZXB0X2l4KSAvIG51bmlmKSAqIDEwMAojIHRoaXMgaXMgOCUKI1hfa2VwdF9peCA8LSB3aGljaChhYnMocHJlZF91bmlmJG1lYW4pIDwgMC4xKQoKCmBgYAoKCiMjIElucHV0IHNwYWNlIHdpdGggbG93IEF0bW9zcGhlcmljIEdyb3d0aCBFcnJvcgpUaGlzIHBhaXJzIHBsb3Qgc2hvd3MgdGhlIDJkIGFuZCBtYXJnaW5hbCBkZW5zaXR5IG9mIGVtdWxhdGVkIGlucHV0IHBvaW50cyB3aGVyZSB0aGUgZW11bGF0ZWQgYXRtb3NwaGVyaWMgZ3Jvd3RoIGlzIGNsb3NlciB0byB0aGUgb2JzZXJ2YXRpb25zIHRoYW4gdGhlIHN0YW5kYXJkIG1vZGVsLgoKVGhpcyB0ZWNobmlxdWUgbWlnaHQgcHJvdmlkZSBhIHVzZWZ1bCBzZXQgb2YgcG9pbnRzIGZvciBvcHRpbWlzaW5nIHRoZSBtb2RlbCAoYXQgbGVhc3QgdG8gYXRtb3NwaGVyaWMgZ3Jvd3RoKS4KCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyLCB3YXJuaW5nID0gRkFMU0V9CiNwYWlycyhYX3VuaWZbWF9rZXB0X2l4LCBdWzE6NTAsXSwKIyAgICAgIGNvbCA9IG1ha2VUcmFuc3BhcmVudCgnYmxhY2snLDUwKSwKIyAgICAgIGdhcCA9IDAsCiMgICAgICB4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpLAojICAgICAgcGNoID0gMTksCiMgICAgICBjZXg9IDAuOCwKIyAgICAgIGxvd2VyLnBhbmVsID0gTlVMTCkKCnBhcihvbWEgPSBjKDAsMCwwLDMpLCBiZyA9ICd3aGl0ZScpCgoKcGFuZWwuaGlzdCA9IGZ1bmN0aW9uKHgsIC4uLikgewogIHVzciA9IHBhcigidXNyIik7IG9uLmV4aXQocGFyKHVzcikpCiAgcGFyKHVzciA9IGModXNyWzE6Ml0sIDAsIDEuNSkpCiAgaGlzdCh4LCBmcmVxID0gRkFMU0UsIGNvbD0iY3lhbiIsIGFkZD1UUlVFKSAKICBsaW5lcyhkZW5zaXR5KHgpKQp9CgpwYWlycyhYX3VuaWZbWF9rZXB0X2l4LCBdLAogICAgIyAgbGFiZWxzID0gMTpkLAogICAgICBnYXAgPSAwLCBsb3dlci5wYW5lbCA9IE5VTEwsIHhsaW0gPSBjKDAsMSksIHlsaW0gPSBjKDAsMSksCiAgICAgIHBhbmVsID0gZGZ1bmNfdXAsCiAgICAgIGRpYWcucGFuZWwgPSBwYW5lbC5oaXN0LAogICAgICBjZXgubGFiZWxzID0gMSwKICAgICAgY29sLmF4aXMgPSAnd2hpdGUnLAogICAgICBkZnVuY19jb2wgPSByYikKCmBgYAoKTmV4dCwgY2hlY2sgZW11bGF0b3JzIG9mIGFsbCB0aGUgb3RoZXIgb3V0cHV0cyBhbmQgYXBwbHkgdGhlIGNvbnN0cmFpbnRzIHRvIHRoZW0uIFNlZSBob3cgdGhlIGNvbnN0cmFpbnRzIGNoYW5nZS4KCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyfQoKCiMgRmlyc3QsIHRyeSB0aGUgZW11bGF0b3JzIGJ1aWx0IHVzaW5nIGp1c3Qgd2F2ZTAxCiNmaXRfbGlzdF9jb25zdF9sZXZlbDFhCgoKWV9jb25zdF9wcmVkX3VuaWZfbWVhbiA8LSBtYXRyaXgoTkEsIG5jb2wgPSBuY29sKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpLCBucm93ID0gbnJvdyhYX3VuaWYpKQpjb2xuYW1lcyhZX2NvbnN0X3ByZWRfdW5pZl9tZWFuKSA8LSBjb2xuYW1lcyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKQoKWV9jb25zdF9wcmVkX3VuaWZfc2QgPC0gbWF0cml4KE5BLCBuY29sID0gbmNvbChZX2NvbnN0X2xldmVsMWFfc2NhbGVkKSwgbnJvdyA9IG5yb3coWF91bmlmKSkgCmNvbG5hbWVzKFlfY29uc3RfcHJlZF91bmlmX3NkKSA8LSBjb2xuYW1lcyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKQoKZm9yKGkgaW4gMTpsZW5ndGgoZml0X2xpc3RfY29uc3RfbGV2ZWwxYSkpewogIAogIHByZWRfdW5pZiA8LSBwcmVkaWN0LmttKG9iamVjdD1maXRfbGlzdF9jb25zdF9sZXZlbDFhW1tpXV0sIG5ld2RhdGEgPSBYX3VuaWYsIHR5cGUgPSAnVUsnKQogIAogIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLGkgXSA8LSBwcmVkX3VuaWYkbWVhbgogIFlfY29uc3RfcHJlZF91bmlmX3NkWyxpIF0gPC0gcHJlZF91bmlmJHNkCn0KCgpgYGAKCiMjIElucHV0IHNwYWNlIHdpdGggZW11bGF0ZWQgbWVtYmVycyBwYXNzaW5nIExldmVsIDIgY29uc3RyYWludHMuCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMiwgd2FybmluZyA9IEZBTFNFfQoKbGV2ZWwyX2l4X2VtIDwtIHdoaWNoKFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCduYnBfbG5kX3N1bSddID4gMCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ25wcF9ubGltX2xuZF9zdW0nXSA+IDM1ICYgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ25wcF9ubGltX2xuZF9zdW0nXSA8IDgwICYKICAgICAgICAgICAgICAgICAgICBZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnY1NvaWxfbG5kX3N1bSddID4gNzUwICYgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NTb2lsX2xuZF9zdW0nXSA8IDMwMDAgJgogICAgICAgICAgICAgICAgICBZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnY1ZlZ19sbmRfc3VtJ10gPiAzMDAgJiBZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnY1ZlZ19sbmRfc3VtJ10gPCA4MDAKICAgICAgICAgICAgICAgICAgKQoKcGFpcnMoWF91bmlmW2xldmVsMl9peF9lbSwgXSwKICAgICMgIGxhYmVscyA9IDE6ZCwKICAgICAgZ2FwID0gMCwgbG93ZXIucGFuZWwgPSBOVUxMLCB4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpLAogICAgICBwYW5lbCA9IGRmdW5jX3VwLAogICAgICBkaWFnLnBhbmVsID0gcGFuZWwuaGlzdCwKICAgICAgY2V4LmxhYmVscyA9IDEsCiAgICAgIGNvbC5heGlzID0gJ3doaXRlJywKICAgICAgZGZ1bmNfY29sID0gcmIpCgoobGVuZ3RoKGxldmVsMl9peF9lbSkgLyBudW5pZikgKiAxMDAKYGBgCgojIyBJbnB1dCBzcGFjZSB3aXRoIGVtdWxhdGVkIG1lbWJlcnMgcGFzc2luZyBMZXZlbCAyIGNvbnN0cmFpbnRzIEFORCBsb3cgYXRtb3NwaGVyaWMgZ3Jvd3RoIGVycm9yCkVtdWxhdGVkIG1lbWJlcnMgcGFzc2luZyBsZXZlbDIgY29uc3RyYWludHMgQU5EIGhhdmluZyBsb3dlciBlcnJvciBpbiBhdG1vc3BoZXJpYyBncm93dGggdGhhbiBzdGFuZGFyZC4gCgpSZWQgcG9pbnQgaW5kaWNhdGVzIHRoZSBzdGFuZGFyZCBpbnB1dC4KClRoZSBwb3NpdGlvbiBvZiB0aGUgc3RhbmRhcmQgaW5wdXQgd2l0aCByZWdhcmRzIHRvIHRoZSBoaXN0b2dyYW1zIGdpdmUgdXMgYW4gaWRlYSBvZiB3aGF0IHdlIG1pZ2h0IGRvIHRvIGltcHJvdmUgdGhlIHNpbXVsYXRpb24gLSBhdCBsZWFzdCBpbiB0ZXJtcyBvZiBhdG1vc3BoZXJpYyBncm93dGguIElmIHdlIHdlcmUgdG8gbW92ZSB0aGUgaW5wdXQgdG93YXJkcyBhcmVhcyBvZiBoaWdoZXIgZGVuc2l0eSwgd2UgbWlnaHQgZXhwZWN0IGEgYmV0dGVyIG1vZGVsIHBlcmZvcm1hbmNlLgoKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyLCB3YXJuaW5nID0gRkFMU0V9CgpYX3N0YW5fbm9ybSA8LSBub3JtYWxpemUobWF0cml4KHJlcCgxLCAzMiksIG5yb3cgPSAxKSwgd3J0ID0gbGhzKQoKbGV2ZWwyX2FuZF9hZ19peF9lbSA8LSB3aGljaChZX2NvbnN0X3ByZWRfdW5pZl9tZWFuWywnbmJwX2xuZF9zdW0nXSA+IDAgJgogICAgICAgICAgICAgICAgICAgIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPiAzNSAmIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCAmCiAgICAgICAgICAgICAgICAgICAgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfY29uc3RfcHJlZF91bmlmX21lYW5bLCdjU29pbF9sbmRfc3VtJ10gPCAzMDAwICYKICAgICAgICAgICAgICAgICAgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NWZWdfbG5kX3N1bSddID4gMzAwICYgWV9jb25zdF9wcmVkX3VuaWZfbWVhblssJ2NWZWdfbG5kX3N1bSddIDwgODAwICYKICAgICAgICAgICAgICAgICAgICBhYnMocHJlZF91bmlmX2FnJG1lYW4pIDwgYWJzKGFnX21vZGVybl9tZV9zdGFuKQogICAgICAgICAgICAgICAgICApCgoKCnBhaXJzKHJiaW5kKFhfdW5pZltsZXZlbDJfYW5kX2FnX2l4X2VtLCBdLCBYX3N0YW5fbm9ybSksCiAgICAjICBsYWJlbHMgPSAxOmQsCiAgICAgIGdhcCA9IDAsIGxvd2VyLnBhbmVsID0gTlVMTCwgeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSwKICAgICAgcGFuZWwgPSBkZnVuY191cF90cnV0aCwKICAgICAgZGlhZy5wYW5lbCA9IHBhbmVsLmhpc3QsCiAgICAgIGNleC5sYWJlbHMgPSAxLAogICAgICBjb2wuYXhpcyA9ICd3aGl0ZScsCiAgICAgIGRmdW5jX2NvbCA9IHJiKQoKCihsZW5ndGgobGV2ZWwyX2FuZF9hZ19peF9lbSkgLyBudW5pZikgKiAxMDAKYGBgCgoKCgo=